개발 블로그: 카카오
올피드를 시작하고 피드를 구독해 보세요.
피드를 구독하면 글이 업데이트 될 때 알림을 받을 수 있습니다. Google과 Apple 계정으로 빠르게 로그인 할 수 있습니다.
LLM, 더 저렴하게, 더 빠르게, 더 똑똑하게
robin.hwang · 10일 전
안녕하세요. 카카오 기술전략 소속의 로빈입니다. 현재 저는 카카오의 AI 교육 자문 활동 맡아 AI 교육 커리큘럼 설계 및 감수를 진행하고 있으며, AI Native Company로의 여정을 위해 AI를 활용하는 사내 시스템을 구축하고 있습니다. 또한 사내의 ChatGPT 커뮤니티를 운영하며, 생성형 AI와 관련한 다양한 트렌드와 소식을 사내 크루들에게 전달하고 있기도 합니다. 많은 분들이 AI 서비스를 구현할 때 가장 고민되는 것 중에 하나가 바로 '...
아파치 플링크와 CDC의 만남. 플링크 CDC 맛보기
louis.sml · 15일 전
안녕하세요, 데이터분석플랫폼 조직의 루이스입니다. 저희 조직의 주요 미션은 서비스 팀들의 데이터를 취합하여 일단위의 지표를 추출하고 제공하는 것입니다. 이를 위해 다양한 소스에서 데이터를 수집하고 있으며, 그 과정에서 서비스 조직의 데이터베이스에 있는 데이터를 활용해야 하는 경우도 있습니다. 그러나 서비스 조직의 데이터베이스는 실제 서비스에 사용되고 있기에, 지표 추출을 목적으로 서비스 조직의 데이터베이스에 접근하여 데이터를 가져오는 방식은 실 서비스의...
AI 시대와 로봇 - 카카오테크가 만난 조규진 교수님
kakao tech · 23일 전
✍️ 요약 카카오의 기술 리더들이 로봇 분야의 권위자 조규진 교수님과 함께 ‘AI시대에 만난 로봇’ 세미나를 진행했습니다. 세미나에서는 로봇 기술의 글로벌 트렌드와 앞으로의 중요성, 그리고 하드웨어 기반 서비스에 대한 고민들을 논의했습니다. 조규진 교수님은 로봇 생태계의 구축과 미래 기술을 대비하기 위해 장기적인 비전과 조직 문화의 변화를 짚어 주셨으며, 카카오의 기술 리더들은 하드웨어 기술의 도입과 확장에 대한 관심을 나누었습니다. ‘AI시대에 만...
모든 기술은 처음이 있었다 - 카카오테크가 만난 Andi Gutmans
kakao tech · 한 달 전
✍️요약 우리 앞에 주어진 모든 기술과 환경은 낯선 처음이 있었습니다. 웹과 인터넷이 태동하고, 모바일 혁명과 디지털 전환의 시대를 거쳐 AI가 열어가는 새로운 세상까지, IT 역사의 흐름을 직접 겪어온 기술 리더들이 티타임을 가졌습니다. 예전의 변화 앞에서 했던 도전, 오늘의 변화 앞에서 또다시 맞이하는 고민들을 공유합니다. PHP의 주역, 웹 개발의 선구자이며, 현재 구글 클라우드 데이터베이스를 총괄하는 🔵 앤디 구트만스(Andi Gutmans, 이하 Andi)와, 카카오의 기술을 총괄하는 🟡 정규돈 CTO(이하 GD)가 7월 30일, 카카오의 판교아지트에서 티타임을 가졌습니다. 구글 클라우드의 모니카 굽타(Monica Gupta, 이하 Monica) 시니어 디렉터와 수바시 구아다드(Subhash Guaddad, 이하 Subhash) 디렉터, 카카오의 데이터플랫폼 조직을 이끄는 오민석 리더(이하 Vincent) 등 기술 리더들이 함께했어요. IT 기술이 태동하고 변화했던 시대를 거쳐온 리더들의 대화가, 또다시 변화 앞에 선 개발자 여러분께 의미 있는 메시지가 되면 좋겠습니다. Era of Web and internet: 역사의 시작 웹과 인터넷이 등장한 1990년대에 두 분은 무엇을 하고 있었나요? 그 시기의 고민과 가장 도전적인 과제는 무엇이었는지 들려주세요. 🔵 Andi: 1990년대면 저는 대학에서 컴퓨터공학을 공부하고 있었는데요, 공부하는 것보다 코딩을 더 좋아했습니다. 그래서 친구와 함께 수업을 듣는 대신 프로젝트를 하기로 했습니다. 당시로서는 꽤 선진적이었던, 쇼핑카트 웹사이트를 만드는 프로젝트였고, 지도교수님이 있었어요. 친구 Zeev가 hosting provider를 작업하면서, PHP/FI를 알게 되었고, 우리는 그 언어를 사용해서 쇼핑카트 웹사이트를 만들기로 했어요. 한 시간쯤 개발하다가 ‘우리가 이 언어를 더 잘 만들 수 있겠다’고 생각했어요. 그때 저희는 대학에서 막 컴파일러 수업을 이수한 상태였거든요. 그래서 쇼핑카트 프로젝트는 잠시 치워두고 이 언어를 다시 만들기로 했어요. 그렇게 PHP3가 탄생했습니다. PHP3를 들고 지도교수님께 가서, 우리가 새 언어를 만들었으니 이것을 새 프로젝트로 받아주실지 물어봤는데 거절하셨어요. 그래서 저희는 컴파일러 수업을 하셨던 교수님께 갔어요. 그 교수님은 이스라엘 IBM research의 리더이기도 했는데 흔쾌히 프로젝트를 맡아주기로 하셨어요. 그래서 저희는 PHP3를 들고 PHP/FI를 사용하는 사람들에게 가서, 우리가 새 버전을 만들었으니 여기로 옮겨오는 것이 어떨지 제안했고, 작은 커뮤니티였지만 사람들이 PHP3를 써주기 시작했어요. 그렇게 일 년 반 만에 약 4만 개의 웹사이트가 150만 개로 증가했고, 정말 재미있었어요. 그리고 잘 아시듯이 PHP3를 오픈소스로 공개했어요. 그런데 PHP3를 오픈소스로 런칭하고 보니 여전히 ‘이것보다도 더 잘할 수 있겠다’는 생각이 들었어요. 그래서 저희는 바로 PHP4를 만들기 시작합니다. PHP4는 PHP3와 달리, 엔진을 분리하기로 했습니다. 그리고 분리한 엔진에는 Zend(Zeev+Andi)라는 이름을 붙였어요. Java에 JVM이 있는 것처럼 PHP에는 Zend 엔진이 있는 거예요. 아무튼 이 Zend엔진이 PHP4를 만들었고, 이 PHP4를 기반으로 야후, 페이스북 등이 만들어졌어요. 🟡 GD: 웹과 인터넷이 부상하기 시작한 때가 우리나라는 IMF 기간이라, 개인적으로 가장 도전적인 과제는 취업이었습니다(농담반 진담반). 우연히 소프트웨어 쪽으로 진로를 정하고 ‘라이코스(Lycos)’라는 초기 포털에서 ‘JMS(Java Message Service)’라는 메시징 미들웨어 개발을 하게 됐어요. 여기서 재미난 경험을 하게 되는데, 당시에는 Solaris라는 상용 서버 운영체제로 인프라가 구성되어 있었고 Linux는 거의 쓰이지 않고 Java도 초기라 관련 개발자가 없던 시절이었어요. 그런데 소프트웨어 쪽은 오픈소스 커뮤니티가 부상하기 시작했고, 저도 우연히 ‘OpenJMS’의 컨트리뷰터로 조인하게 되었습니다. 상용이 아닌 프로덕트가 개발되고 공유되는 형태를 경험하면서 굉장히 인상적이었어요. 당시만 해도 윈도우 세상이었고, 당연히 소프트웨어도 하드웨어와 같이 비용을 지불하고 쓰는 세상이었는데(물론 해킹버전이 많았지만ㅎ), 오픈소스를 기반으로 하는 소프트웨어는 그것과는 거리가 먼 세상이었던 거죠. OpenJMS는 ‘SourceForge’라는 (오늘날의 GitHub과 비슷한) 오픈소스 플랫폼이 있었던 것 같아요. 이런 환경에서 개발자들은 세상의 개발자들과 함께 실력과 경험을 쌓기 좋았고, 저도 오픈소스 커뮤니티의 개발자들과 소통하면서 OpenJMS를 바탕으로 자체 메시징 미들웨어를 개발할 수 있었습니다. 그 당시의 개발자 커뮤니티와 오픈소스는 어떤 모습이었는지도 궁금한데요, 오픈소스 커뮤니티와 관련된 경험을 조금 더 들려주세요. 🟡 GD: 아파치 재단의 오픈소스 중에 기억에 남은 것은 ‘아발론(Avalon)’이라고 하는 컴포넌트 기반 서버 프레임워크예요. 당시에는 지금의 쓰레드풀, 커넥션풀이 따로 라이브러리화 되지 않고 직접 만들어 쓰던 시절이었는데요, 경력 있는 개발자들은 자기만의 라이브러리를 다 가지고 있어서 제가 주니어일 때는 선임이 바뀔 때마다 그분들의 라이브러리를 익히는 과정이 필요했어요. 그런데 오픈소스는 그런 개별적인 수고를 덜어줄 수 있었죠. 아발론 프레임워크를 기반으로 스토리지 서버를 만들었는데, 컴포넌트로 서버의 기능을 나누고 생명주기를 관리하는 법을 배웠고, 쓰레드풀에 버그가 있어서 커뮤니티에 물어보면서 고쳐서 썼던 기억이 있네요. 솔직히 얘기하면 당시 이런 무료 오픈소스가 오래 갈거라 생각하는 사람들은 주변에 많진 않았습니다. 그런데 결과를 보면 이런 시스템, 문화가 지금의 빅테크 기업을 만든 원동력이 됐어요. 소프트웨어 산업에만 있는 특이한 문화라고 볼 수 있어요. 지식을 회사나 개인의 소유로만 가두지 않고 같이 공유하는 문화가 인터넷의 태동기의 정신과 어울려 발전했던 시기를 초기부터 보고 경험했던 부분은 큰 행운이었다고 생각합니다. 지금은 깃헙을 중심으로 오픈소스 커뮤니티가 발전하고 현재 화두가 되는 LLM 시장에서도 오픈소스가 이렇게 쏟아져 나오는 걸 보며 놀라움을 금치 못하고 있어요. 이런 환경에서 개발자 생태계는 더욱 발전하고 또 그런 개발자들이 공헌하는 오픈소스 프로덕트는 더욱 발전하는 선순환을 만들게 되는 것이죠. 과거와 현재를 관통하는 열정 🟡 GD: Andi는 컴퓨팅 언어와 컴파일러의 전문가였는데, 데이터베이스 전문가로 커리어를 바꾸셨잖아요. 두 분야는 많이 다른데, 어떻게 전환이 가능했는지 궁금해요. 🔵 Andi: PHP4를 공동 제작했을 때, Zend 회사를 시작했었는데, 2015년에 매각했어요. 아주 오랜 시간이 걸렸네요. Zend를 매각하고 다음 할 일을 고민하고 있던 중에, 친구가 데이터베이스 부문의 리더십 직책을 고려해보라고 했어요. 저는 당시에 이용자로서만 사용해봤지, 데이터베이스의 내부 사정에 대해서 잘 몰랐거든요, 그런데 그냥 와서 배우면서 일을 해보라고 했어요. 데이터베이스에는 시스템 측면과 쿼리 프로세싱 측면 두 가지가 모두 있잖아요. 저는 시스템을 다루는 사람(System person)이기 때문에 시스템이 어떻게 돌아가는지 잘 이해할 수 있었어요. 제가 데이터베이스 관련해서 처음 했던 프로젝트는 새로운 그래프 데이터베이스 생성을 주도하는 것이었어요. 저는 직관이 좋고 아주 빠르게 배우는데요, 모르는 것을 부끄러워하지 않고 아주 많은 질문을 했어요. 감사하게도 주변에 훌륭한 엔지니어들이 있었기 때문에 그분들에게 쿼리 프로세싱, 쿼리 최적화와 같이 제가 궁금한 것들을 모두 질문하곤 했어요. 데이터베이스 분야에서 최고의 사람들과 팀으로 함께하면서 빠르게 배울 수 있었던 것이 행운이었어요. 저는 학문적인 데이터베이스 전문가는 여전히 아니고 단지 분산 시스템, 스토리지, 쿼리 프로세싱 같은 것들이 뭔지 알고 있을 뿐이에요. 그렇지만 제가 생각하는 저의 강점은 핵심 시스템(core system)이고, 언제나 새로운 것을 배우는 것을 좋아하고, 질문하는 것을 부끄러워하지 않는 그런 점들인 것 같아요. 지금까지도, 제 주변에는 저보다 훨씬 잘하고 항상 새로운 것을 가르쳐 주는 엔지니어들이 많이 있으니까요. 웹과 인터넷이 열어준 시대는 곧이어 시작한 모바일 혁명과 클라우드 컴퓨팅 등의 부상으로 빠르게 다음 단계로 나아갔어요. 과거에서 현재로, 이야기를 한 단계씩 건너와 볼까요? 🟡 GD: 모바일 혁명이 만든 24시간 열린 인터넷 세상은 모든 산업을 빨아들이면서 엄청난 데이터를 축적했어요. 이런 연결선 상에서 각 산업의 디지털 트랜스포메이션은 2010년대 후반에 주목을 받기 시작했는데요. 이때 저는 카카오뱅크 CTO로 기존 은행을 모바일 은행으로 바꾸는 프로젝트를 했어요. 2017년 오픈한 카카오뱅크는 우리나라에서 최초로 영업점이 없는 모바일로만 가능한 은행이었고 당시에 이 정도로 많은 고객들이 사용하는 메이저 은행으로 발전할지 누구도 생각하지 못했어요. 왜냐하면 은행은 여전히 오프라인 거점 중심일 거라는 생각이 지배적이었으니까요. 그리고 모바일 사용성이 이미 은행계좌가 있는 사용자들을 전이시킬 거라고 생각하지 못했어요. 하지만 결과는 달랐어요. 1주일에 100만, 3개월에 500만 이후 1000만까지 빠른 속도로 고객이 모이기 시작했거든요. 이 시점 이후부터 우리나라 금융시장은 디지털 트랜스포메이션을 중요한 과제로 두고 빠르게 변화했습니다. 이런 디지털 트랜스포메이션은 영업사원이 패드를 쓴다고 바뀌는 것도 아니고, 리눅스나 오픈소스를 쓴다고 바뀌는 것도 아니고, 조직과 문화에 걸친 일이기 때문에 변화에는 시간이 걸리고 있어요. 그런 와중에 AI 시대가 도래한 거죠. 아까 잠깐 말씀드린 PC 시대에서 모바일시대로 넘어오면서 24시간 연결된 인터넷 세상은, AI, 특히 딥러닝, 특히 LLM으로 대변되는 시장에서 가장 중요한 데이터의 기반을 닦게 하는 중요한 계기가 됐던 거라고 보여요. 여전히 디지털 전환은 산업별로 시간차를 두고 진행 중이고 여기에 AI 시대가 오면서 이 두 가지를 단순히 기술의 도입이 아닌 기업의 조직과 문화에 어떻게 접목하고 서비스화 하는지가 앞으로의 10년 내의 큰 변화의 요소가 아닐까 생각합니다. 🔵 Andi: 예전에 우리를 설레게 했던 것(what excited us then)과 지금 우리를 설레게 하는 것(what excites us now)을 잠시 얘기해 볼게요. 예전에 우리를 설레게 하던 것은 ‘지적 호기심’이었습니다. 웹, 모바일 등 새로운 플랫폼이 나왔고, 새로운 플랫폼은 더 나은 무언가를 필요로 했고, 그런 새로운 것을 만들어갈 수 있는 세상이었으니까요. 한편 지금 저를 설레게 하는 것은 ‘Gen AI(생성형 AI)’인 것 같아요. 다만 Gen AI 자체보다 좀 더 개발자의 관점에서 바라본 Gen AI요. 왜냐하면 많은 사람들이 여전히 Gen AI를 데이터사이언스나 모델 훈련과 같은 영역으로 생각하는데요, 사실 Gen AI의 정말 흥미로운 점은 이제 모든 개발자가 AI 어플리케이션을 만들 수 있게 되었다는 사실이거든요. 그래서 제가 데이터베이스에서 관심 있게 살펴보고 있는 것 중 하나가, ‘어떻게 하면 개발자들에게 이런 AI 서비스를 만드는 경험을 줄 수 있을까?’입니다. 마치 웹 시대에 저희가 PHP를 만들었던 것 같은 편리한 경험을, Gen AI 시대의 모든 개발자들에게 제공하고 싶어요. New Era of AI and Data: 진정한 AI 시대의 기업이 되기 위해 🟡 GD: 모바일과 디지털 전환이 한창 진행 중이었는데 갑자기 AI 전환이 시작되면서 이 변화를 어떻게 기회로 잡을 수 있을지 고민이 많아요. PC 시대에서 모바일 시대로 넘어올 때 카카오는 기존 PC 시대의 레거시가 없는 ‘모바일 네이티브’ 회사였기 때문에, 모바일에 최적화된 방식으로 프로덕트를 빠르게 만들 수 있었어요. 이제 AI 시대에 또다시 빠르게 적응하려면, 앞 시대의 레거시와 자산에 집중하지 않고 ‘AI 네이티브(AI native)’로 새롭게 전환해야 한다고 생각했어요. 제 생각에는 AI와 LLM의 변화가 Backend에서부터 시작되는 것처럼 보이기 때문에, 현재 제공되는 서비스들의 모습이 모바일 혁명만큼 급격하게 바뀌진 않을 것 같아요. 하지만 점차, 서비스의 도메인 정보에 접근하는 방식과 인터페이싱을 또 한 번 바꿀 것으로 보입니다. 전문지식은 더욱 쉽게 모델화 되고 인터페이스는 자연어와 모달 기술의 발전으로 내추럴하게 바뀜으로써 음성과 영상 인터페이스는 보다 핵심적인 인터페이스로 바뀌게 될 거예요. 저의 고민은 이런 세상의 필수 기술인 Gen AI를 어떻게 바라보고 준비할지입니다. 빅테크 기업의 플랫폼을 기다릴 것인지, SLM(Small Language Model)같이 소규모 도메인에 특화되게 하는 게 맞는 것인지 아직 안 보이니까요. 그래서 접근방식을 AI native로 삼은 것이고요. 세상의 변화에 기민하게 대응하기 위해선 모바일 시대에 카카오가 그랬던 것처럼, 기업 전반의 시스템부터 DNA까지 AI를 자연스럽게 사용하고 생각하는 그런 기업으로 바꾸면서 세상의 변화에 대응하면 그에 맞는 서비스도 가장 기민하게 내놓을 수 있다는 판단이에요. 그런 의미에서, 모바일 네이티브로 시장에서 성공했던 방정식과, AI native로 새로운 시장에서 성공하기 위한 방정식에 연결고리가 있을 것 같아요. 그것이 무엇일지 찾고 있습니다. Andi는 어떻게 생각하시나요? 🔵 Andi: 데이터베이스에서도 고민하시는 것(AI native)과 똑같은 전환 과정을 거쳐왔어요. 저희 팀원들은 데이터베이스 전문가였지, 굳이 어플리케이션 전문가일 필요가 없었어요. 그래서 저희는 모든 곳에 AI를 주입하는 방법을 고민했어요. 쉬운 길은 아니지만 어떻게 보면 모든 개발자가 성공할 수 있는 기회이기도 했어요. 도움이 되었던 것이 몇 가지 있었는데, 그중 하나는 구글에서 많은 사내 도구들이 AI 기능을 갖추고 있다는 점이었어요. 예를 들면 IDE나 코드 생성기(Code generator), 코파일럿, 디코딩 도구 같은 것들이요. 그래서 사내의 엔지니어들이 새로운 기술로 무엇이 가능한지를 경험할 수 있게 해주는 거예요. 두 번째로는, 일찍이 조직을 위한 비전(vision)과 전략을 세우는 작은 팀을 만들었어요. AI가 어떻게 각 데이터베이스의 일부분이 될 것인가, AI를 어떻게 데이터베이스 조직 전체에 걸쳐서 바라볼 것인가 등을 고민하는 거죠. 쉽지는 않았어요. 대부분의 사람들은 이런 것들을 생각하고 있지 않았거든요. 저희는 매주 회의하는 포럼을 하나 만들었어요. 왜냐하면 저는 이게 정말 중요하기 때문에 매주 우리가 만나서 이것에 대해서 얘기해보았으면 했고, 모든 팀이 AI를 도입하기 위해 무엇을 해야 하는가 등에 대해서도 논의해야 한다고 생각했어요. 진행 상황을 보고, 리소스를 확인하는 등 자세하고 지속적으로 얘기를 했어요. 처음에는 설득의 과정이 필요하지만, 지속적인 관찰과 토론 끝에 구성원들 모두 가능성을 확인할 수 있었어요. 예를 하나 들어드릴게요. 제가 ‘모든 데이터베이스는 벡터 지원(Vector support)을 해야 한다’라고 선언했었어요. 지금은 당연해 보이지만, 2년 전이었으니까 벡터 지원이 다른 이니셔티브보다 더 중요하다는 의견에 모두가 동의하지는 않았어요. 그래서 초기에는 제 노력이 좀 필요했지만, 팀들이 하나둘 결과물을 만들어내고 서로 그걸 지켜보면서 자연스럽게 다른 팀들에서도 관심을 가졌어요. 그리고 처음처럼 제가 나서지 않아도, 이제는 스패너(spanner)의 벡터 지원을 런칭하고, 그런 일들이 계속 일어나고 있어요. 🔵 Andi: AI native 회사가 되기 위한 고민에서 두 가지 핵심 영역은 ‘고객을 위해 AI를 어떻게 사용할 것인가?’ 그리고 ‘사내에서 임직원들과 AI를 어떻게 사용할 것인가?’인 것 같아요. 구글에서는 CEO인 순다르(Sundar)를 비롯한 경영진이 지속적으로 우리가 어떻게 고객경험을 변화시키고 있는지를 여러 예시를 통해서 보여주고 있습니다. 일회성이 아니라 지속적으로, 리더들과 회의 때마다 피쳐나 기능의 데모를 항상 가지고 나와서 임직원들과 공유하고, 회사 밖으로도 이런 데모를 가지고 나가서 계속 보여주는 거죠. 이런 예시를 꾸준히 공유하면 사람들이 더 많은 관심을 가지게 되고, 각자의 서비스에 적용할만한 아이디어를 적극적으로 찾게 돼요. 말로만 듣는 것과 직접 보는 것은 정말 다르거든요. 더 많은 사람들에게 보여주면 더 많은 고객 경험이 바뀔 수 있습니다. 반면 임직원들에게는 조금 달라요. 직원들은 아직 AI를 사용할 준비가 되지 않아서 어떻게 도입해야 할지 모르는 경우도 있어 처음에는 다소 회의적일 수 있어요. 하지만 더 중요한 일들에 더 많은 시간을 사용할 수 있도록 AI가 어떻게 도울 수 있는지 - 예를 들어 개발자가 보일러플레이트 코드(boilerplate code)를 단순히 작성하고 있다면 그런 것은 AI를 사용하고, 개발자는 아키텍처나 더 어려운 고민에 집중할 수 있다는 사실을 보여주면, AI가 얼마나 혁신적인 기술인지 깨닫기 시작해요. 그러니까 임직원들과 AI를 사용할 때는, ‘생산성을 높이자’라는 방식보다는 ‘더 재밌고 효율적으로 일하자’라는 방향으로 그런 문화를 만드는 것이 중요합니다. AI가 당신의 일을 뺏는 것이 아니라 당신의 일이 더 재밌어지도록, 당신이 더 많은 일을 할 수 있도록 도와줄 것이다, 라는 방식으로 접근해야 하는 것 같아요. State of the art Database 자연스레 구글클라우드의 데이터베이스들과 오픈소스, 카카오의 데이터베이스 구조에 대한 이야기도 나누었는데요, 한 가지만 전해드립니다. 🟠 Vincent: 구글클라우드는 이미 스패너(Spanner) 데이터베이스가 있고, 스패너가 PostgreSQL 인터페이스도 지원하고 있는데요, 왜 Alloy DB를 출시했나요? 🔵 Andi: AlloyDB를 만들 때 저희가 염두에 둔 고객은 레거시 데이터베이스를 제거하려는 분들이었어요. 많은 저장 프로시저(stored procedure)를 가진 고객들도 포함이에요. 오픈소스 PostgreSQL가 거의 비슷하게 지원해서 더 나은 가용성과 확장성을 만들어주긴 하지만요, 스케일 업에 조금 더 초점을 둔 모델이에요. 스패너에서는 페타바이트(PB) 수준의 트랜잭션 처리를 지원하기 때문에 전통적인 저장 프로시저와 같은 기능은 사용할 수 없어요. 그리고 스패너가 PostgreSQL 인터페이스를 가지고 있긴 하지만 100% 호환이 되지는 않아요. 모든 확장 프로그램이나 저장 프로시저 등을 다 지원하지는 않아요. 그래서 고성능 PostgreSQL을 지원하고 레거시 데이터베이스를 제거하기 위한 지원이 필요했던 거예요. AlloyDB는 저장 프로시저를 지원하고, PostgreSQL을 코드 변경 없이 그대로 마이그레이션 할 수 있어요. 마치며 지금까지 Andi, GD, Vincent, Monica, Subhash와 함께 약 30년간의 IT 기술의 변곡점을 회상하며, 각 시대의 도전과 경험, 그리고 또다시 새로운 변화를 맞이는 고민을 이야기했습니다. 우리 앞에 주어진 모든 기술과 환경은 낯선 처음이 있었습니다. 여전히 역사의 한 페이지를 살아가며, 다음 변화가 올 때 우리는 오늘의 고민을 어떻게 회상할지 궁금해집니다. 또 그런 재미있는 이야기로 돌아오겠습니다! Directed by Yang.me, Hunter.jo Photo by Derek.s Written by Sue.cream
2024 카카오 인턴십 뉴크루들의 기술온보딩 여정
kakao tech · 2달 전
글쓴이 소개 sky.j 안녕하세요. 카카오 기술기획에서 테크 영입/성장을 담당하고 있는 sky(스카이)입니다. 테크직군 크루들의 기술성장을 위한 다양한 프로그램을 운영합니다. 👏 eve.y2k 안녕하세요. 카카오 애플리케이션을 모니터링하기 위한 표준 시스템을 개발하는 모니터링기술의 eve(이브)입니다. 다양한 챌린지가 가득한 카카오에서 개발할 수 있게 되어 기쁘게 생각합니다 ☺️ helen.one 안녕하세요. 카카오 인턴십 과정을 마치고 FE 개발로 합류한 다음채널개발의 helen(헬렌)입니다. 다음채널개발 팀의 팀원으로서 기여할 수 있도록 성장하고 싶습니다 🙌 깃헙 링크에서 뉴크루들의 기술 온보딩의 코드를 확인하실 수 있습니다! 👍 1. 들어가며 안녕하세요. 카카오 기술기획에서 테크직군의 성장을 고민하는 sky입니다. 올해 카카오에는 매우 큰 이벤트가 있었습니다. 바로 2024년 카카오 채용 연계형 겨울 인턴십이 진행이 되었다는 것인데요, 약 두 달간 진행된 겨울인턴십을 마친 인턴분들께서 드디어 카카오의 정규 크루로 입사를 하게 된 것입니다. 이렇게 입사한 카카오 뉴크루들을 위해 저희는 크루분들이 각각 현업으로 가기 전 Backend 기술 온보딩을 진행하였습니다. 뉴크루들이 가진 열정에 멋진 체계적인 기술 역량 한 스푼을 더한다면, 이후 현업에 돌아가서도 무서운 속도로 적응과 성장을 할 수 있다고 믿기 때문이죠. 뉴크루들은 Backend 온보딩 기간 동안 자바와 스프링을 중심으로 TDD와 객체지향, 그리고 WAS 구현을 통한 프레임워크의 이해 등 다양한 분야의 지식을 배우며 경험 및 성장하게 되는데요, 그럼 지금부터 Backend 온보딩을 매우 우수한 성적으로 마친 eve.y2k와 helen.one의 생생한 경험과 성장 과정을 통해 여러분들과 함께 나누어 보고자 합니다. 2. 2024 겨울 인턴십 소개 2024 겨울 인턴십은 채용 연계형으로 진행된 프로그램이에요. 약 두 달간의 프로젝트에 참여하여 과제를 수행하고, 나아가 카카오의 문화와 일하는 방식 등을 경험할 수 있습니다. 또한 이 과정에서 나에게 맞는 기술과 직무에 대해 고민을 해 볼 수 있는 소중한 시간이기도 합니다. 프로젝트 및 인턴십을 마치게 되면, 전환 인터뷰를 통해 정규 크루로 입사하게 되는 프로세스로 진행이 됩니다. 이번 겨울인턴십에 대해 보다 더 많은 궁금증이 있으시다면 2024 카카오 채용 연계형 겨울 인턴십 Coming soon! 에서 확인 해 주세요! 3. 기술 온보딩은 이렇게 진행됐어요🚀 (eve & helen) 뉴크루 기술 온보딩은 미션을 받고, 미션의 요구사항을 만족하는 애플리케이션을 개발하는 과정으로 이뤄졌습니다. 교육 과정이 진행되는 4주 동안 총 3개의 미션을 일정한 단계를 반복하며 진행했습니다. 그 과정을 간략히 요약하면, 일단 미션의 기능적 요구사항과 기술적 요구사항을 듣고 이를 구현하는데 필요한 개념을 강의를 통해 학습했습니다. 강의는 프로그래밍 이론뿐만 아니라 클린 코드 기반의 자바 프로그래밍에 익숙해질 수 있도록 구성되었습니다. 그 뒤 페어 프로그래밍 및 개별 작업을 통해 진행한 미션을 github 리포지토리에 제출하고, 리뷰어 분과의 리뷰 과정을 통해 개선점을 찾고 코드를 리팩토링하는 시간을 가졌습니다. 여기에 더불어 온보딩 조원들과 함께 데일리 회고와 주간 회고를 통해 아쉬운 부분이나 개선점을 찾아나갈 수 있었습니다. 이제 각 단계가 구체적으로 어떤 내용으로 진행되었는지 소개해드릴게요. 미션 설명 및 개념 강의 뉴크루 기술 온보딩 과제는 시작부터 끝까지 강의와 함께 진행됐습니다. 강의 내용은 과제에 대한 설명, 그리고 과제를 완수하는데 필요한 개념으로 구성되어 있었습니다. “과제 설명" 학습 목표, 범위, 미션의 요구사항 등을 자세히 설명해 주셨습니다. 특히 과제를 해결하며 겪었던, 혹은 앞으로 겪게 될 고민이나 어려움을 함께 고민하고 공유해 주셔서 많은 도움이 되었습니다. 강의 방식에서 흥미로웠던 부분은, 강의가 단순히 개념을 주입받는 시간이 아니라, IDE 창을 화면에 공유하며 실시간으로 라이브코딩을 하는 과정 속에서 크루들과 적극적으로 소통하는 시간이었다는 점입니다. 요구사항을 정리하고, 패키지와 클래스 구조를 설계하고, 예외를 처리하는 부분까지 모든 과정은 TDD로 진행되었으며, 어떤 클래스를 먼저 만들고, 어떤 기능을 어떻게 테스트할 것인지 이야기를 나누는 토론 시간이 되었습니다. 강의에서는 TDD(Test-Driven Development), OOP(Object-Oriented Programming), 아키텍처 설계 원리 및 원칙을 비롯해 웹 애플리케이션 서버의 구조와 HTTP의 동작 원리까지 다양하고 폭넓은 주제를 다루었습니다. 다소 딱딱할 수도 있던 이론 설명 시간 후에 직접 HTTP cookie 값을 설정하고 활용하여 브라우저 상에서 웹 작동 과정을 조작하는 자바 코드를 작성하고, 이를 페어와 함께 실시간으로 테스트하여 통과시키는 실습을 진행했습니다. 미션 소개 미션은 총 세 가지로, 후반부 미션으로 갈수록 요구 사항이 많아지고 지켜야 할 규칙도 많아졌습니다. 각 미션 별로 정해진 PR 제출 기한이 있었기 때문에 많은 뉴크루가 미션 완성도와 마감 기한 사이에서 적절히 타협해 나가는 모습을 보였던 것 같습니다. ① 자동차 경주 미션 처음 진행된 자동차 경주 미션은 제한된 횟수에서 랜덤 생성한 숫자에 따라 자동차의 전진 여부를 결정하며 우승한 자동차를 결정하는 미션이었습니다. 오직 한 단계의 들여쓰기 만을 사용해야 하는 규칙이나 else 예약어를 사용할 수 없는 규칙 등의 제한을 만족해야 했고, 테스트 코드를 작성한 뒤 도메인 코드를 작성하는 순서로 진행해야 했습니다. 또한 교육 과정의 첫 미션이면서 첫 TDD, 그리고 첫 페어 프로그래밍이라는 낯섦에 적응해야 했기 때문에 쉽지 않았던 경험이었습니다. 메인 패키지의 클래스를 만들기 전, 테스트 패키지에서 먼저 테스트 코드를 작성할 때 IDE에 표시되는 빨간 줄을 먼저 보고 개발을 시작하는 것이 새로웠던 기억이 있네요. ② 로또 미션 바로 다음 미션이었던 로또 미션은 2단계로 진행이 되었는데요. 1단계에서는 자동으로 로또를 생성하는 기능만 제공했지만, 2단계에서는 사용자가 수동으로 추첨 번호를 입력해야 한다는 요구사항의 변화에 대응해야 했습니다. 로또 미션을 진행하면서부터는 ‘객체지향 생활체조 원칙’에 따른 더욱 엄격한 규칙을 지켜야 했는데, TDD에 이어 이러한 규칙들까지 적용하며 페어프로그래밍을 해야 하니 이전 미션보다도 진행이 더뎠습니다. 뿐만 아니라 사용자의 입력에 대한 예외 케이스를 처리하기 위해 시간을 많이 사용했던 기억이 나네요. 이래저래 난관이 많았지만, 낯선 것에 계속 도전하며 요구사항을 해결해 나가는 과정을 통해 한층 성장한 기분을 느낄 수 있었습니다. ③ WAS 미션 마지막은 WAS 구현 미션이었습니다. 웹 프레임워크의 도움 없이 HTTP 기반의 웹 서버를 직접 구현하며, HTML과 CSS 등의 정적 리소스를 제공하는 기능부터 로그인 처리, 사용자 요청에 따른 데이터 조작, 동적인 콘텐츠 제공까지 웹 어플리케이션 서버가 제공하는 다양한 기능을 구현해야 했습니다. 이 과정에서 HTTP 헤더와 바디, 요청과 응답 라인, 쿠키와 세션 등 웹의 주요 구성 요소를 TCP 위에서 직접 파싱하고 객체화하는 작업을 수행했습니다. WAS 미션을 통해 단순한 프로그래밍 능력의 향상뿐만 아니라 웹 기술의 핵심을 이해하고, 스프링 MVC와 같은 웹 프레임워크가 왜 현재의 아키텍처를 선택했는지를 더 깊이 있게 이해할 수 있었습니다. 페어프로그래밍 미션은 전부 페어와 함께 2인 1조로 진행되었다고 말씀드렸는데요. 대부분의 페어가 자바 프로그래밍에 상대적으로 익숙한 Server 직군, 그리고 FE와 Infra 등 그 외 직군의 조합으로 이뤄졌습니다. 이번엔 특별히 온보딩 교육 기간 동안 페어로써 함께 프로그래밍을 진행한 저희 helen과 eve의 대화 형식으로 소감을 들려드리겠습니다. eve.y2k : 헬렌은 처음 페어프로그래밍을 한다고 했을 때 어떠셨나요? 걱정되는 부분이 있지는 않았나요? 🤔 helen.one : 저는 서버 직군이 아니어서 처음에는 어떻게 진행해야 할지 막막한 부분이 있었어요. 드라이버와 내비게이터의 역할을 번갈아가며 코드를 작성하는 방식이었는데, 자바의 문법이나 컨벤션이 익숙하지 않다 보니 그런 부분에서 이브에게 많은 도움을 받을 수 있을 것 같았어요. 그러나 반대로 이브에게는 어떤 도움을 줄 수 있을지 걱정이 되었어요. 이브는 어떠셨나요? eve.y2k : 저는 서버 직군으로서 작성한 모든 코드에 대해 설명을 해야 한다는 부담감이 조금 있었던 것 같아요. 하지만 헬렌의 이해가 빠르고 적극적으로 질문해준 덕분에 기존에 애매하게 알고 있던 내용이나 깊이 있게 숙지하지 못했던 언어와 프레임워크의 기능들을 복습하고 더 깊이 있게 이해할 수 있었어요. 그리고 페어가 다른 직군이어서 코드를 작성할 때나 컨벤션을 적용할 때 더욱 다양한 의견을 주고받으며 훨씬 넓은 시야로 문제를 바라보고 해결책을 모색하는 경험을 할 수 있었어요. helen.one : 저도 궁금한 부분을 이브에게 바로바로 질문하며 빠르게 배울 수 있었고, 그 덕분에 교육 후반에는 자바 언어나 TDD, OOP적인 설계에 익숙해진 것을 체감하며 학습에 즐거움을 느꼈던 것 같아요. 특히나 함께 진행한 자동차 경주나 로또 미션이 기억에 남는데, 간단한 과제라고 생각했지만 실제로는 고려해야 할 부분이 매우 많았고, 함께한 덕에 더 깊이 있게 문제를 파악하고 더 나은 솔루션을 찾아가며 만족스러운 코드를 작성했다고 생각해요. eve.y2k : 맞아요. 로또 미션을 진행하는 과정에서 초반에는 테스트 코드의 작성이나 클래스 설계에서 어려움을 많이 느꼈고 속도가 더딘 부분이 있었는데, 그 단계를 지나고 나서는 요구사항에 따라 기능이 빠르게 완성되는 모습을 보고 OOP의 장점을 실감했던 것 같아요. 페어로 진행했기에 더욱 의미가 있었던 것 같은데, 헬렌은 앞으로도 페어 프로그래밍을 적극적으로 활용해 보실 의향이 있으신가요? helen.one : 네! 페어 프로그래밍을 하면서 단기간에 서로에게 많은 것을 배우고 성장할 수 있는 좋은 기회가 됐던 것 같아 기회가 된다면 꼭 적용해보고 싶어요 👍 PR, 리뷰 반영 기술 온보딩에서는 실습을 통해 git과 github의 사용법을 익히고 리뷰를 통해 코드 개선과 성장을 이룰 수 있었습니다. 미션은 깃 저장소를 clone 한 후, 개별 브랜치에서 작업을 진행하면서 컨벤션에 맞게 커밋 메세지를 남기고, PR을 통해 받은 리뷰를 반영하여 리팩토링하는 과정을 포함했습니다. 리뷰는 총 2단계로 진행되는데, 1차는 페어와 함께한 코드를, 2차는 1차 리뷰 코멘트를 바탕으로 개인이 리팩토링한 코드를 리뷰받았습니다. 함께 작성한 코드에 대해 각자가 다른 리뷰어의 리뷰를 받으며 더 다양한 의견을 듣고 고민할 수 있었던 것이죠. 페어의 코드뿐만 아니라 온보딩 교육에 참여한 다른 뉴크루들의 리뷰와 리팩토링 과정 역시 볼 수 있었기 때문에 더욱 다양한 구현 방식과 피드백을 접할 수 있어 정말로 유익했던 것 같습니다. 코드 리뷰는 강의와 관련된 내용 위주로 진행되었습니다. 리뷰어 분들은 객체지향 생활 체조 원칙을 지켰는지, 확장과 변화에 유리한 구조인지, 의미 있는 테스트 코드를 빠짐없이 잘 작성했는지 등을 중점적으로 확인해 주셨습니다. 코드 전반적인 부분을 꼼꼼히 리뷰해 주신 덕분에 기능을 구현할 때 주의해야 하는 것이 무엇인지 알 수 있었고, 단순히 사용하던 언어 라이브러리도 성능을 고려하여 공식 문서를 확인하며 더 적절한 구현체나 메소드로 바꿔 사용하기도 했습니다. 회고 온보딩 교육 과정 전반적으로 데일리 회고, 주간 회고, 최종 회고를 함께 진행했습니다. 이 시간에는 뉴크루들이 공유하는 스프레드시트에 미션을 수행하며 좋았던 점, 아쉬웠던 점, 앞으로의 마음가짐 등을 간단히 적고 팀원들과 함께 회고하며 각오를 나누었습니다. 특히 데일리 회고를 통해 전날의 아쉬웠던 점을 바로바로 공유함으로써 다음 날은 동일한 실수를 줄일 수 있었고, 전날 학습한 내용을 복습하며 더욱 깊이 이해할 기회가 되었습니다. 서로가 전날 한 것들과 오늘 할 것들을 공유하며 잘해나가고 있는지 점검하니 하루하루를 유익하게 살고 있다는 뿌듯함도 얻을 수 있었습니다. 또한 팀원들 각자가 이해하고 학습한 경험과 고민을 서로 공유하는 것만으로도 많은 간접 경험을 할 수 있었던 것 같아요. 주간 회고는 ‘감정 회고’라는 키워드로 진행되었는데요. 과제의 진행 과정이나 느낀 점보다는 페어 프로그래밍을 하면서 좋았던 감정, 아쉬웠던 감정, 그리고 회고에 대한 소감과 앞으로의 다짐 등을 팀원들과 공유하는 시간이었습니다. 이런 과정을 통해 서로의 감정을 공유하고 이해하는 것은 물론, 서로가 주는 피드백을 긍정적으로 받아들이며 좋은 협업의 자세도 갖춰나갈 수 있었다고 생각합니다. 4. 기술온보딩을 마치며 온보딩 과정을 마치며 온보딩 과정을 통해 TDD, 객체지향 원칙, 페어 프로그래밍 등 이론적으로는 알고 있었지만 실제로 적용해 보기는 처음인 개념들을 경험할 수 있었어요. 처음에는 낯설고 적용하기 어려웠지만, 페어와 함께 컴포트 존에서 벗어나 새로운 것을 학습해 나가는 과정이 유익했던 것 같습니다. 미션에 대한 리뷰어 분들의 코드 리뷰는 정말 많은 도움이 되었습니다. 단순히 정답을 제시한 것이 아니라, 문제가 될 수 있는 부분을 짚어주고 어떻게 해결할 수 있는지 묻는 질문을 통해 깊이 있게 생각하고 좋은 해결책을 찾도록 이끌어주었습니다. 특히 실무에서 흔히 보이는 “트레이드오프” 상황에서 어떤 것을 중점으로 생각해야 할지에 관한 많은 인사이트를 얻을 수 있었습니다. 또한, 강의에서 배운 프로그래밍 관련 내용뿐만 아니라 팀에 소속된 한 명의 개발자로서 신뢰 자본을 쌓는 법, 팀에 좋은 변화를 만드는 법 등 실무에 합류한다면 갖춰야 할 개발자로서의 마인드셋 역시 배우고 실천해 볼 수 있었습니다. 온보딩 교육을 담당해 주셨던 분들도 교육에 참여한 교육자로서, 그리고 선배 개발자로서 필요한 이야기를 많이 해주셨는데, 재밌고 유익한 내용들이 많아서 많은 도움이 되었습니다. 앞으로 어려움에 부딪혔을 때 어떻게 극복해야 하는지, 어떤 방식으로 개발에 임해야 하는지 등을 배울 수 있었습니다. 온보딩 과정은 개발자로서 성장하고 팀에 기여하기 위한 단단한 기반을 마련할 수 있는 소중한 시간이었습니다. 이를 바탕으로 앞으로도 끊임없이 노력하여 긍정적인 영향력을 주는 카카오 크루가 될 수 있었으면 좋겠습니다. 두 달간의 인턴십 과정부터 4주 간의 기술 온보딩까지, 함께 달려온 뉴크루 여러분 모두 고생 많으셨습니다 🙌 카카오 크루가 된 앞으로의 다짐과 각오 helen.one 카카오에서 개발자로서의 첫 시작을 함께 하는 만큼, 얼마나 많이 배우고 도전해 볼 수 있을지 기대가 되는 것 같아요. 앞으로도 개발에 대한 즐거움이나 성장하고자 하는 욕심을 잃지 말고 팀에 도움이 되는 개발자가 되겠습니다 😸 eve.y2k 아직은 욕심만큼 능력이 뛰어나진 못하지만, 배울 수 있는 것들을 최대한 배우고 할 수 있는 것들을 최대한 해보려고 해요. 이렇게 훌륭한 크루들이 많은 카카오에서 일할 수 있는 것을 좋은 기회로 여기고 저 역시 훌륭한 크루의 일원이 되겠습니다 😄
카카오테크 부트캠프 2024 입과식, 인재들의 첫 걸음 🎊
eddy.kim · 2달 전
판교의 미래를 이끌어갈 새로운 인재들이 한자리에 모였습니다. 지난 7월 1일, 카카오테크 부트캠프 판교 강의장에서 열린 카카오테크 부트캠프 2024 입과식 현장의 뜨거운 열기를 전해드립니다. 카카오테크 부트캠프란? 카카오테크 부트캠프는 SW 인재양성 교육으로 고용노동부와 카카오가 함께 만들어가고 있습니다. 6개월 동안 160여명의 학생들이 3개의 과정(풀스택, 인공지능, 클라우드)의 교육을 온라인 + 오프라인으로 참여합니다. 카카오테크 부트캠프 강의장은 판교의 개발자 문화를 간접체험할 수 있는 공간으로 최신의 교육환경을 제공합니다. 꿈과 열정이 가득한 시작 오전 9시, 160여 명의 예비 개발자들이 설렘과 기대를 안고 입과식장에 모였습니다. 다양한 배경을 가진 참가자들의 얼굴에서는 새로운 도전에 대한 흥분과 약간의 긴장감이 엿보였습니다. John.k (로컬&플랫폼기술 성과리더)의 환영사로 시작된 입과식은 카카오의 비전, 카카오테크 부트캠프의 목표를 공유하는 것으로 이어졌습니다. John.k은 "여러분이 카카오테크 부트캠프를 통해 IT 산업의 핵심 인재로 성장하길 바란다"며 참가자들을 격려했습니다. 혁신적인 커리큘럼 소개 이어서 구름의 류성태 대표님이 6개월간의 카카오테크 부트캠프 프로그램을 소개했습니다. 인공지능, 풀스택, 클라우드 등 최신 기술 트렌드를 반영한 커리큘럼은 참가자들의 큰 관심을 끌었습니다. 특히 실제 프로젝트 경험을 쌓을 수 있는 ‘서비스 개발 프로젝트’ 세션에 대한 기대가 높았습니다. 이 과정에서 참가자들은 카카오의 현직 개발자들과 협업하며 실무 능력을 키우게 됩니다. 미래를 향한 첫걸음 공식 행사 일정이 종료된 후, 참가자들은 조별로 모여 서로를 소개하고 앞으로의 계획을 나누는 시간을 가졌습니다. 처음 만난 동기들과 어색한 웃음을 주고받으며, 앞으로 6개월간의 여정에 대한 기대감을 나누었습니다. 카카오테크 부트캠프는 단순한 교육 프로그램이 아닙니다. 대한민국 IT 산업의 미래를 이끌어갈 인재들이 꿈을 키우고 실력을 쌓아가는 성장의 장이 될 것입니다. 이들의 열정적인 시작을 응원하며, 앞으로의 눈부신 성장을 기대해 봅니다. 📸 렌즈에 담긴 열정: 카카오테크 부트캠프 입과식 포토 리포트 이곳이 바로 카카오테크 부트캠프 강의장이에요. 교육하기에 완벽한 이 온도, 습도, 조명… 카카오테크 부트캠프 멘토가 되어 교육생들과 호흡해 보세요! 곧 카카오테크 부트캠프 멘토풀 모집이 예정되어 있으니, 많은 관심 부탁드려요! 🙏 다시 입과식 현장!!! 오전 9시 입과식을 앞두고 모두 설레는 마음으로 교육생들이 강의장에 입장하고 있습니다. 교육의 시작은 출첵!!! 한 명도 빠짐없이 출첵하고 입장~~~ 🤹🏼♀️ John.k의 환영사로 입과식 행사가 시작되었어요. 존의 진심 어린 환영사가 교육생들에게 오랫동안 기억에 남았으면 좋겠네요. 사진으로도 느껴지는 교육생들의 뜨거운 열정! 순수한 열정이 느껴져서 운영진들도 좋은 에너지를 많이 얻었답니다.❤️🔥❤️🔥❤️🔥❤️🔥❤️🔥 첫째 날부터 이런 텐션이라니~ 여러분들의 열쩡 열쩡 열쩡, 6개월 동안 쭉 이어가길 응원해요 👏 행사 마무리 국룰은 손가락 하트 단체사진 📽 오늘 너무 수고 많았어요. 우리 6개월 동안 카카오테크 부트캠프에서 여러분의 멋진 꿈을 키워 봅시다. 🎤 기대와 설렘 가득, 카카오테크 부트캠프 신입생들의 생생한 소감 💻 인공지능 과정 / 김민지(ally) 안녕하세요! 민지입니다. 카카오테크 부트캠프의 오리엔테이션은 다른 정말 인상 깊었던 것 같아요. 다양한 배경을 가진 친구들과 함께 할 수 있어서 너무 설렜고, 앞으로의 여정이 더욱 기대돼요. 특히 오리엔테이션에서 다양한 사람들과 어울릴 수 있는 게임이 많이 준비되어 있어서 더 뜻깊은 시간이 되었어요. 그리고 오리엔테이션에서 진행한 특강 덕분에 AI 개발자로서 가져야 하는 마인드에 대해 많이 배우게 되었고, 덕분에 저도 제 목표를 더 확실하게 다지게 되었어요. 특강에서 들었던 것처럼 specialist도 좋지만, 저는 generalist가 되어 다양한 분야에서 두루두루 능력을 발휘할 수 있는 사람이 되고 싶어요. 앞으로 부트캠프 과정에서 배울 것도 많고 도전할 사항도 많겠지만, 열심히 노력해서 꼭 성장하는 모습을 보여드리고 싶습니다! 🖥 클라우드 과정 / 심혜수(heather) 안녕하세요 클라우드 과정 수강 중인 heather라고 합니다. 애플리케이션 배포 과정에서 클라우드에 관심을 갖게 되었고, 마침 클라우드 과정이 포함된 카카오테크 부트캠프에 지원하게 되었습니다. 오리엔테이션에서 우선 놀라웠던 부분은 새롭게 단장한 쾌적하고 깔끔한 환경이었습니다. 첫 기수로서 이런 환경에서 공부할 수 있다는 것이 참 기뻤습니다. 또한, 생성형 AI 시대의 개발자 방향성에 대한 특강은 인상 깊었으며, "6개월 동안 우리가 여러분의 소속이 되어 준다"는 말이 기억에 남습니다. 아이스브레이킹 시간을 통해 프로젝트 팀원들과 화합을 다지고, 즐겁게 게임을 진행하며 서로 친해질 수 있었습니다. 부트캠프에 적응하는 단계이지만, 프로젝트와 실습 위주로 진행되는 과정에서 다양한 내용을 배우고 있습니다. 강의를 들으며 모르는 부분은 멘토링을 통해 해결해 나가고 있어, 학습에 큰 도움이 되고 있습니다. 이 과정을 통해 다양한 환경에서 프로젝트를 성공적으로 수행할 수 있는 전문성 있는 개발자로 거듭나고, 확장성 있는 애플리케이션을 개발할 수 있는 역량을 키우고 싶습니다. 카카오테크 부트캠프에서의 경험을 통해 한층 더 성장하는 개발자가 되겠습니다. 감사합니다!
주니어 FE 개발자의 색상 추출 라이브러리 개발기
eve.christmas · 2달 전
개요 안녕하세요, 카카오 FE플랫폼 조직에서 광고SDK 업무를 담당했던 이브입니다. 이 글에는 광고 프로젝트를 담당하던 주니어 개발자가 색상 추출 라이브러리를 개발하게 된 과정을 담았습니다. 이 글에 포함되는 내용은 다음과 같습니다. 광고 효율을 높이기 위한 기획자의 아이디어를 구체화하여 사내 라이브러리로 배포하기까지의 과정을 소개합니다. 색상 추출에 사용한 K-means Clustering 알고리즘의 작동 원리와 실제 적용 결과를 소개합니다. 기획자, 디자이너 등 타 직군과의 소통을 위해 스토리북을 활용한 사례를 소개합니다. 문제정의 카카오 광고 템플릿 소개 카카오에서는 광고 사업을 활발하게 진행하고 있습니다. 카카오 FE 조직에서는 자신의 페이지에 광고를 노출하려는 매체들이 사용할 수 있는 광고SDK 뿐만 아니라, 광고 동영상 플레이어, 광고 템플릿과 같이 광고와 관련된 여러가지 FE 프로젝트를 담당하고 있습니다. 그 중 광고 템플릿 프로젝트는 광고주가 광고 이미지와 문구만 등록하면, 카카오에서 미리 정의한 다양한 템플릿으로 조합하여 광고를 노출하는 프로젝트입니다. 카카오 광고 템플릿을 사용하면 다음과 같은 장점이 있습니다. 광고주가 완성된 형태의 광고를 등록할 필요가 없이, 광고에 필요한 최소 정보(광고 문구, 이미지 등)만 등록합니다. 이처럼 광고 등록 과정을 간소화하여 광고주의 부담을 줄이고, 광고주가 광고 캠페인에 더 집중할 수 있게 합니다. 카카오에서 검수한 디자인에 따라 광고 지면에 알맞은 크기와 형태로 광고가 노출됩니다. 이는 광고주에게 안정적인 광고 노출을 보장하고, 오랫동안 카카오에서 축적한 데이터와 노하우를 활용한 템플릿으로 광고 효율을 향상합니다. 광고 색상 추출 작업의 목표 기획에서 이러한 광고 템플릿에 광고 이미지와 어울리는 색상을 채워주면 어떨까 하는 아이디어를 제안하셨습니다. 이렇게 하면 광고주가 등록한 에셋으로 채워지지 않는 여백을 최소화하면서 전체 광고가 일관성 있게 보여지는 효과를 기대할 수 있었습니다. 광고주가 광고에 노출할 문구와 이미지를 등록할 때, 광고의 빈 곳에 채워질 색상을 추가로 등록하도록 하면 이 아이디어는 쉽게 구현될 수 있습니다. 그렇지만 색상이 채워진 광고의 효과가 입증되지 않은 상태에서 카카오의 고객인 광고주에게 이를 요청하는 것은 위험성이 있다고 판단하였습니다. 따라서 우선 광고주에게 별도의 색상을 요청하지 않고, 광고를 그려주는 템플릿에서 광고 이미지를 기반으로 적절한 색상을 채워주도록 구현한 후 광고 효과를 측정해 보기로 하였습니다. 대표 색상의 정의 이러한 배경을 바탕으로 ‘광고 이미지의 대표 색상을 자동으로 추출하는 템플릿 코드 작성’이라는 태스크가 기획에서 개발로 넘어오게 되었습니다. 그런데 ‘대표 색상’이라는 정의가 생각보다 쉽지 않았습니다. 아래에 두 가지 예시를 들어보겠습니다. 첫 번째, 만약 이미지 전체의 수치적 평균을 계산해 보면 어떤 결과가 나올까요? 대상 이미지를 아주 멀리서 바라보면 전체 픽셀 RGB의 수치적 평균과 유사한 결과를 얻을 수 있습니다. 만약 전체 이미지가 비슷한 톤으로 이루어져 있다면 이미지를 대표할 만한 결과를 얻겠지만, 다양한 색으로 이루어진 이미지의 경우에는 생뚱맞은 결과를 얻을 수 있습니다. 예시로, 그림3의 첫 번째와 두 번째 이미지의 경우 그럴듯한 결과를 보이지만, 세 번째와 네 번째 이미지의 경우 이미지에는 없는 색상이 도출되는데요, 마치 이미지의 모든 색상을 한 팔레트에서 섞은 것과 같습니다. 원본 이미지에는 존재하지 않는 색상이기 때문에 대표 색상이라고 하기에는 어색합니다. 두 번째, 전체 픽셀 RGB 값 중 가장 많이 등장한 값, 즉 최빈값을 추출해보면 어떤 결과가 나올까요? 단색 위주의 이미지라면 괜찮은 결과를 얻을 수 있을 것입니다. 하지만 사람의 눈에는 비슷해보이지만 그라데이션과 같이 실제 RGB 값에 미세한 차이가 있는 색상들이 주류를 이루는 이미지라면 전혀 다른 결과를 얻을 수 있을 것입니다. 예시로, 그림4의 첫 번째와 두 번째 이미지의 경우 단색 위주로 이루어진 이미지이기 때문에 그럴듯한 결과를 보입니다. 그러나 세 번째 이미지의 경우 가장 많은 영역을 차지하는 것처럼 보이는 초록색 계열의 색상이 아닌, 전혀 생뚱맞은 색상을 반환합니다. 또한 세 번째, 네 번째 이미지의 결과값을 보면 매우 유사하게 보이는 색상들이 줄지어 있는 것을 확인할 수 있습니다 위 두 가지 예시를 종합하여 보면, 사람이 보기에는 비슷한 색상이지만 실제 RGB 값은 조금씩 다른 픽셀들을 하나의 그룹으로 묶은 후, 가장 많은 픽셀이 포함된 그룹의 색상을 선택한다면, 좀 더 괜찮은 결과를 얻을 수 있지 않을까요? 문제 해결 K-means Clustering 알고리즘 색상 정보는 RGB 또는 HSV 라는 3차원의 값으로 표현할 수 있습니다. 3차원의 색상 정보 간 ‘거리’를 활용하여, 거리가 짧은 픽셀끼리 같은 그룹으로 묶는다면 색상들을 그룹화할 수 있을 것으로 보였습니다. 이러한 문제를 데이터 과학에서 ‘군집화’ 문제라고 합니다. 군집화는 서로 유사한 데이터들은 같은 그룹으로, 서로 유사하지 않은 데이터는 다른 그룹으로 분리하는 것입니다. 군집화에서 중요한 두 가지 문제는 다음과 같습니다. 몇 개의 그룹으로 묶을 것인가 데이터의 ‘유사도’를 어떻게 정의할 것인가 군집화 문제를 푸는 알고리즘 중 가장 널리 쓰이는 K-means Clustering 은 위의 두 가지 문제를 다음과 같이 풀어냅니다. ‘K’ 라는 인자를 사용해 사용자가 몇 개의 그룹을 만들 것인지 설정합니다. ‘Means’ 는 각 데이터로부터 데이터가 속한 그룹 중심까지의 평균 거리를 의미하며, 이 거리가 짧을수록 유사한 데이터가 됩니다. 이 값을 최소화하는 것이 K-means Clustering 알고리즘의 목표입니다. K-means clustering 알고리즘의 작동 원리는 다음과 같은 반복적인 접근이 핵심입니다. K 개의 임의의 중심점(centroid)을 배치한다. 각 데이터를 가장 가까운 중심점(centroid)의 군집(cluster)으로 할당한다. 새롭게 만들어진 각 군집의 중심점을 업데이트한다. 위의 2, 3번 과정을 반복한다. 이를 그림으로 살펴보면 다음과 같습니다. 설정값: K=2 중심점을 랜덤 초기화합니다. 모든 데이터를 가장 가까운 중심점의 군집으로 할당합니다. 각 군집의 중심점을 업데이트합니다. 모든 데이터를 가장 가까운 중심점의 군집으로 할당합니다. 각 군집의 중심점을 업데이트 합니다. 4,5 과정을 반복해도 더이상 중심점이 달라지지 않기 때문에 종료합니다. [그림7.1 - 7.5] K-means clustering 알고리즘 예시 K-means clustering 알고리즘은 위와 같은 방식으로 동작합니다. 이 동작과 함께 유의해야 할 사항은 다음과 같습니다. 최초의 중심점 초기화에 따라 결과가 달라집니다. 따라서 중심점 초기화에 랜덤 선택, K-means++ 기법 등의 다양한 전략이 존재합니다. k값을 정하는 데에도 다양한 방법을 시도해 볼 수 있습니다. 원하는 군집의 개수가 있다면 이를 k값으로 사용할 수 있습니다. 원하는 군집의 개수가 없다면 Elbow Method, Silhouette Method와 같은 방법으로 이상적인 k값을 결정할 수 있습니다. 중요한 것은 다양한 조합으로 시도해 보고, 그중 가장 이상적인 결과값을 반환하는 파라미터를 선택하는 것입니다. 일반적으로는 더 이상 중심점이 달라지지 않을 때까지 중심점 할당과 업데이트를 반복하지만, 무한 루프에 빠지지 않도록 최대 업데이트 횟수에 제한을 둡니다. 시간 복잡도는 일반적으로 O(I * K * d * n) 입니다. 변수는 다음과 같습니다. I=반복 횟수, K=군집의 개수, d=데이터의 차원 색상 추출 알고리즘 적용 위에서 소개한 K-means Clustering 알고리즘을 광고 템플릿 내의 코드로 구현하여 광고 이미지의 색상을 그룹화하였습니다. 이때, 색상 정보의 분류라는 점에서 중심점과 K값을 결정하였습니다. 중심점은 빨간색, 초록색, 파란색, 노란색, 보라색 등의 가장 특징적인 색상들을 사용하였습니다. 다양한 이미지로 테스트해 보며 가장 이상적인 결과가 나오는 K값(군집의 개수)을 디자이너와 논의하여 결정하였습니다. 알고리즘을 적용한 결과는 다음과 같은데요, 이미지 내 색상들이 대체로 잘 군집화가 되고 대표 색상이 잘 추출되는 것처럼 보입니다. 발전 스토리북을 활용한 테스터 K-means clustering 알고리즘을 적용하여 원하는 결과를 얻기까지 디자이너와의 소통이 중요했습니다. 다음과 같은 사항에 대하여 디자이너의 검토가 필요했기 때문입니다. 가장 이상적인 결과를 반환하는 색상 추출 방식 무채색, 원색과 같이 추출에서 제외할 색상의 범위 흰색 또는 검은색 중 어울리는 문구 색상 일반적으로는 개발 결과를 디자이너가 검토하고 의견을 전달하면, 개발자가 다시 개발에 반영하는 방식으로 소통할 수 있는데요, 이러한 방식을 여러 번 반복하는 것은 많은 시간이 소요되고 서로의 피로감이 높을 것으로 예상되었습니다. 따라서 개발자와 디자이너 간의 빠른 소통이 가능한 방법을 고민하였고 스토리북(Storybook.js)을 활용한 웹 테스트 도구를 만들어 디자이너에게 전달하였습니다. 스토리북은 패널에 원하는 값을 넣어 테스트할 수 있는 기능을 제공합니다. 이를 활용하여 패널에 테스트할 이미지의 url, 추출할 색상의 채도 및 밝기 범위, 다양한 색상 추출 방식을 선택할 수 있도록 개발하였습니다. 테스터를 사용하면 디자이너는 테스트할 이미지 url을 넣어 색상 추출의 결과를 바로 확인할 수 있습니다. 또한 다양한 옵션으로 테스트한 후 가장 마음에 드는 결과를 바로 개발자에게 전달할 수 있습니다. 이러한 소통 방식으로 디자인 검토 또는 개발 반영을 기다리는 딜레이를 줄일 수 있었고 불필요한 소통 과정을 줄일 수 있었습니다. 사내 라이브러리로 개발 최종적으로 디자이너가 검수를 마친 옵션을 적용하여 실제 광고 템플릿에 광고 이미지의 대표 색상이 채워지도록 배포하였습니다. 이후 A/B 테스트를 통하여 광고 배경색의 효과를 측정하였고, 광고의 빈 부분에 색상이 칠해져 있을 때 유의미한 효과가 있다는 것을 확인할 수 있었습니다. 따라서 초기의 기획 의도로 돌아가서, 광고 배경색의 효과가 입증되었으니, 광고주가 카카오 광고 플랫폼에 광고를 등록하는 시점에 광고와 어울리는 색을 광고주에게 요청하기로 하였습니다. 이 때, 광고주가 등록한 광고 이미지를 기반으로 대표 색상을 추출하여 광고주에게 추천한다면, 광고주 입장에서 해당 색상을 가이드 삼아서 보다 쉽게 색상을 고를 수 있을 것입니다. 따라서 광고 템플릿에 적용했던 색상 추출 로직을 이제는 광고주가 사용하는 카카오 광고 플랫폼에 적용하고자 하였는데요, 광고 템플릿은 저희 FE 팀에서 담당하여 개발하고 있지만 카카오 광고 플랫폼의 경우 다른 팀에서 개발하고 있기 때문에 개발 주체가 다르다는 문제가 있었습니다. 따라서 광고 템플릿에 적용한 색상 추출 로직을 다른 팀에서도 사용할 수 있도록 사내 라이브러리 형태로 개발하여 전달하기로 협의하였습니다. 이를 통하여 광고 플랫폼 이외에도 ‘이미지의 대표 색상 추출 로직’을 필요로 하는 사내 다른 팀들의 니즈도 함께 충족할 수 있게 되었습니다. 라이브러리 형태에 적절한 인풋과 아웃풋을 정의하고, 기존 광고 템플릿 내에 적용했던 로직을 라이브러리 형태로 개발하였습니다. 이후 라이브러리 사용 가이드를 작성하여 사내에 배포하고, 광고 플랫폼 개발 측에 전달하였습니다. 이전에 기획자, 디자이너와의 소통을 위하여 스토리북을 활용하였던 것과 마찬가지로, 라이브러리를 테스트할 수 있는 페이지 또한 스토리북으로 개발하여 제공하였습니다. 테스트 페이지를 통해 다양한 옵션을 사용하여 라이브러리를 적용한 결과를 미리 테스트해 볼 수 있습니다. 이후 다양한 팀에서 색상 추출 라이브러리를 사용하게 되면서 기존과는 다른 유즈 케이스를 만족하기 위한 개선 작업을 진행하며 라이브러리의 버전을 올려 나갔습니다. 예를 들어, 광고 템플릿에서는 이미지의 대표 색상 1개만 추출하면 되지만 광고주 플랫폼에 적용될 때에는 광고주에게 추천할 최소 색상 개수를 만족하도록 라이브러리가 지원해야 했습니다. 이러한 경우 옵션 추가 등의 방법으로 대응하며 현재도 계속 업데이트되고 있습니다. 마치며 지금까지 광고의 빈 곳을 이미지의 대표 색상으로 채웠을 때의 광고 효과를 확인하기 위한 작업이 사내 라이브러리 개발로 이어지게 된 과정을 소개해 드렸습니다. 이 과정을 통하여 주니어 개발자로서 문제 정의부터 문제 해결을 위한 알고리즘 구현, 그리고 타 부서의 니즈를 충족하기 위한 테스터 개발 및 소통까지 개발자로서 많은 영역을 경험하고 성장할 수 있었습니다. 독자분들에게도 이 과정이 조금이나마 흥미로우셨기를, 그리고 제가 얻었던 지식과 경험이 전달되었기를 바라며 글을 마칩니다. 긴 글 함께 해주셔서 감사합니다.
스팸 콘텐츠 대응을 위한 카카오의 대규모 언어 모델(LLM) 도입 사례 / 제6회 Kakao Tech Meet
kakao tech · 2달 전
6월 13일에 진행한 제6회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #AI #LLM #SpamDetection #TextClassification #TextGeneration 발표자 zoey(조혜연님) 인터뷰 Q. 발표 주제를 선정한 과정에 대해서 이야기해주세요. 주제를 선택한 이유와 고려 요소가 있을까요? 발표 주제는 스팸 콘텐츠 대응을 위한 LLM 도입 사례인대요. 주제를 선정한 과정에 대해 말씀드리면 저는 스팸이라 부르는 유해 콘텐츠를 차단하는 조직에서 일하고 있어요. 스팸 콘텐츠 대응을 하면서, 저희 조직의 스팸 분류 작업이 어떤 고민을 필요로 하는지 공유하고 싶었어요. LLM을 통해 문장 생성뿐만 아니라 분류 문제도 해결할 수 있다는 점과 LLM의 장점을 활용해 분류 결과를 문장으로 재생성함으로써 스팸을 모니터링하는 운영자들에게 실질적인 도움을 제공하고 있습니다. 이런 과정을 통해 LLM의 다양한 활용 가능성과 사람에게 설명가능한 AI에 대해 전달하고자 이 주제를 선택하게 되었습니다. Q. 발표를 준비하면서 가장 많은 시간을 고민한 부분은 무엇인가요? 참석자들의 LLM에 대한 이해도가 어느 정도인지를 몰라 어떻게 쉽게 설명할지 고민했어요. 또한 스팸 분야 자체가 흔하지 않아 발표 내용을 어떻게 청자들에게 잘 전달할지 고민하며 예시를 많이 사용했습니다. 이런 저의 고민이 청자들에게 잘 전달되었으면 좋겠습니다. Q. 테크밋을 마무리하신 소감을 들려주세요. 인상적이었던 부분이 있나요? 카카오에서 기술적인 이야기를 주제로 발표하는 것은 처음이었는데, 떨리기도 했지만 재미있었습니다. 집중해서 경청해주시는 참석자들의 모습을 보며 더 열심히 발표 내용을 전달해야겠다고 생각했습니다. 발표한 내용이 잘 전달됐는지 궁금했는데, 패널 토의를 통해 추가적인 내용을 전달할 수 있어서 좋았습니다. 상호작용하는 느낌이 정말 좋았어요. Q. 못다 한 이야기가 있다면? 현장에서 들어온 질문 중 하나가 '데이터의 개수가 얼마나 되어야 하나요?'였는대요. 사내 프로젝트를 외부에 발표하다보니 구체적인 수치를 물어보는 질문에 명쾌한 답변을 드리지 못하기도 했지만 사실, 이 질문에는 정답이 없어요. 왜냐하면 우리가 어떤 목표를 가지고 모델을 만들고자 하는지, 어떤 결과를 기대하는지에 따라 필요한 데이터의 양이 달라지기 때문이죠. 이에 대한 해답은 상황에 따라 다를 수밖에 없습니다. 예를 들어, 만약 특정 상황에 모델을 편향시키고 싶다면 그 상황에 맞는 데이터를 더 많이 수집할 수 있고, 반대로 모델이 다양한 상황을 잘 이해하도록 하고 싶다면 가능한 한 많은 데이터를 활용해 다양한 패턴을 학습시키는 것이 좋습니다. '이런 상황에서 어떻게 판단해야 하나요?'라는 질문에 대한 답변도 비슷합니다. 모델러는 모델을 학습할 때 어떤 기준을 세워서 데이터를 수집하고 라벨링합니다. 이 과정에서 '이런 상황’이라는 것이 어느 정도 가이드라인으로 정리될거에요. 예를 들어, 'apple’이라는 단어를 분류할 때 해결하려는 도메인이 주식과 관련 되었다면 '회사’로, 음식이라면 '과일’로 라벨링을 해야할거에요. 이처럼 데이터와 모델을 활용해 문제를 해결하는 과정은 매우 주관적이고, 상황에 따라 다양한 접근이 가능합니다. 이럴 때 명확한 규칙이 있으면 좋겠지만, 실제로는 해당 도메인을 깊이 이해하고, 데이터의 특성을 정밀하게 분석하고 이해하는 과정이 훨씬 중요하다고 이야기드리고 싶어요. 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: 카카오의 스팸 메일 대응 전략: 문자열 변형 CASE STUDY / 제6회 Kakao Tech Meet 이미지 기반 스팸 대응을 위한 카카오의 AI 기술 활용 / 제6회 Kakao Tech Meet 스팸 콘텐츠 대응을 위한 카카오의 대규모 언어 모델(LLM) 도입 사례 / 제6회 Kakao Tech Meet
이미지 기반 스팸 대응을 위한 카카오의 AI 기술 활용 / 제6회 Kakao Tech Meet
kakao tech · 2달 전
6월 13일에 진행한 제6회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #웹뷰 #디버깅 #ChromeDevTools #websocket 발표자 herschel(오창화님) 인터뷰 Q. 발표를 통해 어떤 이야기를 해주고 싶으셨나요? 이미지 분류 모델, 특히 성인 이미지 분류 모델 등의 경험기는 외부에 공개된 노하우들이 매우 적습니다. 그런 부분에 단초를 제공하고 싶었습니다. 그리고… 저는 외부에 딥러닝 발표를 할 때 가장 이야기 하고 싶은 것은 바로, “머신러닝/딥러닝에서 중요한 것은 수학적 지식도 개발적 테크닉도 아닌 끊임없이 생각하고 시도하고 도전하는 것” 입니다. 너무 어려워하지말고 기초가 없어서 힘들다 생각말고 도전했으면 좋겠습니다. Q. 앞으로의 테크밋에 기대하는 것은 무엇인가요? 카카오 와서 아쉽다는 생각을 하던거는 우리 회사가 외부 많은 개발자분들과 이야기 나누는 자리를 만들지 않는다는 것이 아쉬웠습니다. 테크밋은 그 아쉬움을 끊어낼 수 있는 좋은 자리라 생각합니다. 여러 개발 경험에 대한 이야기도 나누고 더 나아가 발표자의 경험을 공유하는것도 좋지만 참여자들과 많은 이야기를 나눌 수 있는 시간이 있으면 좋겠어요. Q. 패널토의에 참여하신 후기가 궁금해요! 외부 발표를 꽤 했던 편인데도 이런 방식은 처음 경험해봐서 매우 신선했습니다. 특히나 궁금하셨던 질문들을 시간 관계상 현장에서 모두 답변드리긴 어려운데 좋아요 기반으로 많은 분들이 궁금해하시는 부분 위주로 답변이 가능하다보니 저도 청중들은 제 발표에 이런걸 궁금해 했구나 라는것을 알 수 있어서 좋았어요. 또한 같이 발표한 크루들과 한 자리 에서 모여서 이야기 하는것도 든든한 동료가 옆에 있어 긴장도 덜 수 있었습니다. 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: 카카오의 스팸 메일 대응 전략: 문자열 변형 CASE STUDY / 제6회 Kakao Tech Meet 이미지 기반 스팸 대응을 위한 카카오의 AI 기술 활용 / 제6회 Kakao Tech Meet 스팸 콘텐츠 대응을 위한 카카오의 대규모 언어 모델(LLM) 도입 사례 / 제6회 Kakao Tech Meet
카카오의 스팸 메일 대응 전략: 문자열 변형 CASE STUDY / 제6회 Kakao Tech Meet
kakao tech · 2달 전
6월 13일에 진행한 제5회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #MAIL #Text #Encoding 발표자 laura(이소민님) 인터뷰 Q. 테크밋에 참여하게 된 계기가 있나요? 실제 케이스를 보면서 이야기할 수 있는 자리를 만들어보고 싶었습니다. 이에 대한 아이디어와 대응 과정을 따라가면서 스스로 회고도 하고, 외부개발자의 시선에서 어떤 생각들을 하실지가 궁금했었어요. 이러한 생각에 맞춰서 발표준비에 임했던것 같습니다. Q. 연단에서 참가자분들을 바라볼 때 어땠나요? 떨리기도 하고… 진중한 태도로 경청해주셔서 감사했습니다. 개인적으로 스팸메일에 큰 관심이 있을까 싶기도 했었는데 생각보다 많은분들이 관심을 갖고 봐주셔서 의외라 생각했었어요. Q. 개인적으로 앞으로 도전해보고 싶으신 것이 있나요? 과거 IFKAKAO를 통해서 메일 카테고리 분류모델 개발에 대해 말씀드린적이 있는데요, 카테고리 뿐 아니라 스팸 도메인에서도 모델을 개발중에 있는데, 외부에 알릴 기회가 생기면 좋겠습니다 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: 카카오의 스팸 메일 대응 전략: 문자열 변형 CASE STUDY / 제6회 Kakao Tech Meet 이미지 기반 스팸 대응을 위한 카카오의 AI 기술 활용 / 제6회 Kakao Tech Meet 스팸 콘텐츠 대응을 위한 카카오의 대규모 언어 모델(LLM) 도입 사례 / 제6회 Kakao Tech Meet
이미지 분류 모델 개발, 아직도 데이터만 기다려?
zayden.lee · 3달 전
안녕하세요. 맵비전플랫폼개발의 Zayden(이동진)입니다. 제가 속한 조직은 카카오맵의 지도 데이터를 고도화하거나 카카오맵에서 활용할 수 있는 비전 시스템을 개발하고 있는데요. 오늘은 이러한 업무를 수행하는 저희 조직에서 진행했던 프로젝트인, 카카오맵의 리뷰 이미지를 분류하는 이미지 분류 모델의 개발 과정을 독자분들께 공유하고자 합니다. 현재 이미지 분류 기술은 딥러닝 분야에서 비교적 간단한 작업으로 간주되며, 다양한 구현 예제를 쉽게 찾아볼 수 있습니다. 이번 글에서는 다양한 예제들 중, Zero-shot 분류를 사용하여 가공한 선분류 데이터셋을 통해 모델 개발 기간을 단축하고, OOD(Out-of-Distribution) 필터링을 이용하여 정의되지 않은 카테고리를 분류해 내는 방법을 중점으로 소개하겠습니다. 이미지 분류 모델 선정 딥러닝 모델을 개발하려면 먼저 수많은 모델 중에서 어떤 모델을 선택할지 결정하는 과정이 필요합니다. 딥러닝 기술이 급속히 발전하던 시기에는 해마다 공개되는 모델 간의 성능 차이가 컸기 때문에, 더 나은 모델을 찾기 위해 수많은 실험을 해야 했습니다. 그러나 현재는 딥러닝 기술이 상당히 성숙한 상황이며, 모델의 종류보다는 개발자가 학습에 사용할 데이터에 초점을 맞추는 것이 더 현명합니다. 이는 모델의 종류보다는 학습 데이터의 양과 품질이 모델의 성능에 더 큰 영향을 끼치기 때문입니다. 당시 진행 중이었던 리뷰 이미지 분류 프로젝트의 요구사항은 ‘실내’, ‘장소 외관’, ‘야외’, ‘음식’, ‘메뉴판’ 및 ‘스팸’의 총 6개의 카테고리를 분류해야 하는 비교적 간단한 작업이었습니다. 따라서, 가볍고 단순하면서도 성능이 좋은 모델을 선택해야 했습니다. 이 조건들을 고려하여, 많은 모델들 중에서도 CVPR 2022에서 공개된 ConvNext[1]라는 순수 컨볼루션 신경망(Convnet) 모델을 선정했습니다. ConvNext 논문의 저자는 컨볼루션 신경망도 최적화 과정을 통해 최신 트랜스포머 모델의 성능을 능가할 수 있다고 주장하기도 했었습니다. 이후, 논문의 실제 결과가 이 주장을 뒷받침하며 ConvNext 모델은 좋은 성능뿐만 아니라 단순한 구조로 경량화되어 효율성 또한 높다는 것이 확인되었습니다. 이러한 이유들로 프로젝트의 주 모델을 ConvNext로 선택하게 되었습니다. 학습 및 평가 데이터 구축 학습 및 평가 데이터를 구축하기 위해서는 먼저 퍼블릭 데이터셋을 찾아보는 것이 좋습니다. 학습 데이터를 구축하는 데 걸리는 시간을 단축할 수 있기 때문입니다. 이번 프로젝트에서 필요한 ‘실내’, ‘장소 외관’, ‘야외’ 등의 데이터는 평범한 이미지기 때문에 공개된 퍼블릭 데이터가 많을 것이라 기대했지만, 원하는 조건의 데이터를 찾는 것은 쉽지 않았습니다. 예를 들어, ‘실내’ 이미지는 음식점, 카페, 전시장 등에서 분위기와 공간감이 충분히 나타나는 이미지여야 했고, ‘장소 외관’의 경우 특정 장소의 간판 등을 포함한 외관의 특징을 갖추어야 하는 조건이 있었습니다. 이처럼 좋은 모델을 만들기 위한 기반이 되는 고품질의 데이터셋은 분류 기준을 충족해야 하며, 최대한 최종 사용자의 데이터와 유사한 분포를 가져야 합니다. 예를 들어, 서양 가정집의 ‘실내’ 이미지로만 학습 데이터를 구축하면, 해당 모델은 한국 카페의 ‘실내’를 제대로 분류하기 어려울 수 있습니다. 이러한 이유로, 저희 조직에서는 카카오에서 수집한 이미지 데이터를 분류하여 학습 및 평가 데이터를 구축하기로 했습니다. 그렇게 약 20만 장의 이미지 데이터를 수집하였으며, 이 이미지들을 분류 기준에 따라 분류해야 했습니다. 물론 요청 시 사내 레이블링 작업자분들이 작업을 진행해 주실 수도 있었겠지만, 20만 장의 이미지를 분류하는 데는 상당한 시간이 소요됩니다. 그 시간 동안 저희 조직에서는 다음 작업을 진행할 수 없는 상황이 되는 것이죠. 학습 데이터 선분류 그래서 저희는 이미 잘 학습된 AI 모델을 활용해 수집한 학습 데이터의 일부를 선분류하기로 했습니다. CLIP[2]과 같은 멀티 모달 모델에 이미지와 텍스트 쿼리를 입력하여, 이미지와 각 텍스트 간의 상관관계를 추출하는 Zero-shot 분류가 가능합니다. 예를 들어, 강아지 이미지와 ‘강아지’, ‘고양이’ 및 ‘자동차’와 같은 텍스트 쿼리를 입력하면 ‘강아지’ 텍스트에 가장 높은 스코어를 출력해 주는 형식입니다. 이와 같은 방식으로, 20만 장의 이미지와 [‘실내’, ‘장소 외관’, ‘야외’, ‘음식’, ‘메뉴판’, ‘스팸’]이라는 텍스트 쿼리를 CLIP 모델에 입력하여 모든 이미지의 의사 라벨(Pseudo label)을 얻을 수 있습니다. 물론 이렇게 단순하게 처리하면 레이블에 상당히 노이즈가 섞이게 되어 데이터셋의 품질이 떨어질 수 있습니다. 따라서 텍스트 쿼리를 구체화하고 일부 샘플 데이터를 테스트하여 텍스트별 정확도를 측정한 후, 정확도가 높은 텍스트만 신뢰하는 방식을 사용했습니다. 또한, 모델의 출력에는 이미지와 텍스트 간의 유사도가 포함되기 때문에, 특정 Threshold 이상의 유사도를 가진 이미지들만 선분류 데이터로 사용했습니다. 결과적으로 음식, 음료, 야외, 자동차 및 동물에 대한 구체적인 텍스트 쿼리에서는 높은 정확도를 얻을 수 있었습니다. 그러나 실내, 건물 외관, 광고 및 웹캡처와 같은 추상적인 개념을 가진 텍스트에서는 정확도가 높지 않았습니다. 그래서 조금 더 구체적인 텍스트 쿼리를 사용했습니다. 예를 들어, 실내의 경우 대부분 의자와 테이블이 포함된 경우가 많기 때문에 ‘의자와 테이블’이라는 쿼리를 추가하는 방식입니다. 결국, CLIP 모델도 이미지와 텍스트 쌍을 활용하여 학습된 모델이기 때문에, 학습에 사용된 이미지와 텍스트와 유사한 경우에만 높은 신뢰도를 가지는 결과를 보여주게 됩니다. 이렇게 선분류한 이미지들도 정확한 데이터는 아니기 때문에, 카테고리별로 폴더에 정리한 후 대략적인 검수 작업을 진행했습니다. 오분류가 많은 일부 클래스는 선분류 데이터로 사용하지 않았으며, 이러한 과정을 거쳐 최종적으로 전체 학습 데이터셋의 약 40%에 해당하는 선분류 데이터를 얻을 수 있었습니다. 이러한 과정을 통해 짧은 시간 안에 상당한 양의 학습 및 평가 데이터를 확보할 수 있었습니다. 이후 레이블링 작업자들이 작업을 하는 동안 선분류 데이터를 활용하여 모델 실험을 진행했고, 레이블링이 완료된 후 전체 학습 데이터로 파인튜닝만을 진행하여 모델 개발을 완료할 수 있었습니다. 결론적으로 선분류 데이터를 활용한 덕분에 개발 기간을 크게 단축할 수 있었습니다. 테스트 데이터셋 구축 테스트 데이터셋을 만드는 과정도 매우 중요합니다. 단순히 전체 데이터의 일부를 테스트 데이터로 분리하는 것만으로는 부족하며, 테스트 데이터를 통해 모델의 성능을 면밀하게 평가하는 것이 최종 목표인 것을 기억해야 합니다. 이를 위해 테스트 데이터셋에는 각 카테고리별로 다양한 형태의 이미지가 포함되어야 합니다. 예를 들어, ‘실내’ 카테고리에는 다양한 음식점, 카페, 전시장, 호텔 및 운동 시설 등의 실내 이미지가 포함되어야 하고, ‘음식’ 카테고리에는 한식, 중식, 일식, 분식, 음료 및 베이커리 등 다양한 종류의 음식이 포함되어야 합니다. 이처럼 테스트 데이터셋을 잘 구성해야만 학습된 모델을 평가할 때 성능을 신뢰할 수 있습니다. 간혹 학습 데이터의 퀄리티에만 집중하다 보면 편향된 테스트 데이터셋이 만들어질 수 있습니다. 이 경우 일부 카테고리의 성능이 과도하게 높거나 낮게 나올 수 있는데요. 이렇게 되면 모델의 개발 과정에서 어느 부분을 어떻게 보완해야 할지 판단하기 어려워질 수 있습니다. 따라서, 테스트 데이터셋을 구성할 때는 다양한 사례를 고려하여 포괄적으로 데이터를 포함시켜야 합니다. 모델 학습 및 평가 작업자 분들이 레이블링 데이터를 작업하는 동안, 앞서 확보한 선분류 데이터로 이미지 분류 모델로 선정한 ConvNext 모델을 학습하였습니다. 최종적으로 Zero-shot 분류로 구축한 선분류 데이터셋 만으로 학습한 모델의 Mean accuracy가 90%를 넘어 나쁘지 않은 수치를 기록했으나, 사실 이는 6개의 카테고리로 진행한 이미지 분류 작업에서는 그다지 높은 성능은 아닙니다. 원인을 분석하기 위해 모델의 Confusion matrix를 추출했습니다. 이미지 분류 시 Mean accuracy만으로는 모델이 어떤 카테고리를 잘못 분류하는지 알기 어렵지만, Confusion matrix를 사용하면, 실제 데이터의 카테고리별로 정답에 대비해 어떤 카테고리로 잘못 분류되었는지 상세히 분석할 수 있기 때문입니다. 결과를 확인하니, 아래와 같이 ‘스팸’ 카테고리가 정답 ‘스팸’이 아닌 다른 카테고리로 분류된 경우가 종종 있었습니다. ‘스팸’ 카테고리는 카카오맵 장소의 후기 및 리뷰 이미지로 사용되지 않길 원하는 스팸성 이미지로 정의했으며, 광고, 포스트, 캡처 이미지, 이모티콘, 만화, 문자 및 카톡 대화내용 및 명함 등의 이미지가 포함됩니다. 더 깊이 들여다보면 실내나 장소 외관 이미지에 광고가 붙어 스팸 카테고리가 되거나 예상치 못한 형태의 스팸성 이미지가 발견되었습니다. 이는 다른 다섯 가지 카테고리에 비해 ‘스팸’ 카테고리의 데이터 분포가 상당히 넓다는 것을 의미합니다. 때문에 저희는 더욱 다양한 스팸성 이미지를 수집하였고, 레이블링 작업 시에도 스팸성 이미지를 철저하게 분류했습니다. 이러한 과정을 통해 전체 데이터로 모델을 학습하면서 최적의 하이퍼파라미터 세팅 값을 찾고, 최종적으로 Mean accuracy를 3% 이상 향상할 수 있었습니다. 이제, 정답을 맞히지 못한 케이스들은 두 가지 이상의 카테고리 특성을 동시에 지닌 이미지들에서만 발생했습니다. 그럼에도 불구하고, 실제 서비스에서 유입되는 사용자 데이터는 데이터 분포가 더욱 다양할 수 있기 때문에, 스팸성 데이터가 ‘스팸’으로 분류되지 않는 경우가 발생할 수 있습니다. 이를 보완하기 위해, ‘스팸’을 보다 보수적으로 분류할 수 있도록 Out-of-Distribution 데이터를 추가로 필터링하는 후처리 기능을 추가했습니다. 모델 비교 모델 개발 과정에서 적절한 모델을 선정하기 위해, 주 모델로 선정한 ConvNext 모델과 ResNet[3] 모델을 모두 학습시킨 후 그 결과를 비교해 보았습니다. 아래 이미지와 같이 두 모델 간 최종 성능의 차이는 크지 않은 것처럼 보이지만, 모델 학습 과정을 살펴보면 상당한 차이가 있다는 것을 확인할 수 있었습니다. 먼저 Training Loss를 비교해 보면, ConvNext 모델이 빠르게 더 낮은 Loss 값으로 수렴했으며, Test accuracy 또한 ConvNext 모델이 훨씬 더 빠르게 상승하는 것을 확인할 수 있었습니다. 이처럼 정량적 지표를 그래프로 시각화하여 비교하면, 어떤 모델이 더 효율적인지 선택하기가 훨씬 수월해집니다. ConvNext 모델은 구조적으로 최신 기술이 반영되어 있어, ResNet 모델에 비해 학습 속도와 수렴 속도에서 뛰어난 성능을 보였습니다. 이러한 점들을 고려하면, 비슷한 최종 성능을 달성하더라도 더 빠르게 학습하고 최적의 결과에 도달할 수 있는 모델을 선택하는 것이 유리합니다. 이러한 비교 분석을 통해, 우리는 ConvNext 모델이 학습 효율성에서 더 우수하다는 결론을 내릴 수 있었습니다. 아직 최종 모델을 선택하기 전이라면 이러한 학습 과정에서의 중간 산출물을 토대로 적합한 모델을 선택하는 데 도움을 받을 수 있습니다. 모델 파라미터 업데이트 시, EMA(Exponential Moving Average) 적용 모델 파라미터 업데이트 시 EMA(Exponential moving average)를 적용하는 실험도 진행했는데요, EMA는 모델 학습 시 파라미터를 스무딩 하여 모델의 안정성과 일반화 성능을 향상하는 기술입니다. 좀 더 구체적으로 설명하자면, EMA는 모델의 파라미터를 업데이트할 때, 과거의 파라미터를 일정 비율로 반영하여 현재 파라미터에 반영하는 방식입니다. 이로 인해 모델 파라미터의 변화를 완화시켜 급격한 변화를 줄이고, 모델의 안정성을 높여 예측이 일관되게 나올 수 있게 합니다. 아래의 그래프를 참고하면, 실제 실험에서도 EMA를 적용했을 때 모델의 성능이 훨씬 안정적이며, 학습이 더 빠르게 수렴하는 것을 확인할 수 있습니다. 이처럼 EMA는 모델이 새로운 데이터에 대해 더 견고하게 반응할 수 있도록 도와주며, 특히 변동성이 큰 데이터에 대해 더 안정적인 학습을 가능하게 합니다. 이러한 이유로, 이미지 분류 모델 개발 시 EMA를 도입하는 것이 매우 효과적이라는 결론을 내릴 수 있었습니다. EMA 적용 여부에 따른 Test accuracy OOD (Out-of-Distribution) Filtering OOD(Out-of-Distribution) 필터링은 모델이 학습하지 않은 데이터 또는 학습 데이터와 매우 다른 데이터를 식별하고 분류하는 과정으로, 앞서 설명한 스팸 분류를 더욱 강력하게 수행하기 위해 적용한 기술입니다. 모델을 학습 후 적용할 수 있는 가장 간단한 방법인 Confidence score 기반 필터링[4]을 적용했으며, 이는 모델의 Softmax 출력 중 가장 높은 값의 크기를 기준으로 특정 Threshold를 설정하여 이를 초과하는 경우 OOD로 판단하는 방법입니다. 이때, 딥러닝 모델의 특성상 Softmax 출력이 모델의 불확실성(Uncertainty)을 정확하게 반영하지 못할 수 있습니다. 이를 보완하기 위해 Confidence score를 Temperature scaling[5]하여 조정합니다. 이렇게 스케일링된 Softmax 스코어 값 중 최댓값이 특정 Threshold 보다 작으면 해당 이미지를 OOD로 판단합니다. 이는 모델이 특정 클래스에 대해 충분히 높은 확신을 가지지 않으면 해당 입력을 OOD로 판단한다는 뜻입니다. 이번 프로젝트에서는 Temperature scaling은 T=1.5로 설정하고, Threshold는 0.5에서 0.8까지 높여 가며 실험했습니다. Threshold를 높일수록 이미지를 OOD로 분류할 확률이 커져 Spam-class의 정확도(Accuracy)는 증가하지만, Mean accuracy는 점차 감소하는 것을 확인할 수 있습니다. 때문에, 실험적으로 적정 Threshold를 구하여 사용하는 것이 좋습니다. 이러한 간단한 OOD 필터링 알고리즘을 적용하면, 모델을 재학습하거나 수정하는 작업 없이 효율적으로 OOD를 필터링할 수 있습니다. 모델 결과 예시 아래의 예시는 학습을 완료한 실제 모델의 예측 결과입니다. 맺음말 지금까지 카카오맵 리뷰 이미지 분류 모델을 개발한 과정을 소개해 드렸습니다. 그간의 딥러닝 모델과 프레임워크의 발전, 그리고 다양한 오픈소스 모델의 공개 덕분에, 불과 몇 년 전과 비교하여도 훨씬 더 효율적으로 딥러닝 모델을 개발할 수 있는 여러 전략이 생겼다는 것을 알 수 있었습니다. 이 글이 딥러닝 모델을 도입하고자 하는 독자분들에게 조금이나마 도움이 되셨기를 바라며, 긴 글 읽어주셔서 감사합니다. Reference [1] Liu, Zhuang, et al. “A convnet for the 2020s.” Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2022. [2] Radford, Alec, et al. “Learning transferable visual models from natural language supervision.” International conference on machine learning. PMLR, 2021. [3] He, Kaiming, et al. “Deep residual learning for image recognition.” Proceedings of the IEEE conference on computer vision and pattern recognition. 2016. [4] Hendrycks, Dan, and Kevin Gimpel. “A baseline for detecting misclassified and out-of-distribution examples in neural networks.” arXiv preprint arXiv:1610.02136 (2016). [5] Guo, Chuan, et al. “On calibration of modern neural networks.” International conference on machine learning. PMLR, 2017. written by Zayden.lee edited by June.6
코틀린을 활용한 안전한 효과 처리
alan.an · 3달 전
안녕하세요, 전자문서 서비스의 서버를 개발하고 있는 Alan입니다. 스프링(Spring)으로 서버를 개발하다 보면 데이터베이스를 사용하는 경우가 곧잘 있습니다. 이때 트랜잭션 처리를 직접 구현하기보다는 스프링의 지원을 받아 구현하는 것이 일반적인 과정입니다. 특히, 트랜잭션의 처리 방식을 정확하게 알지 못하더라도 프레임워크가 모든 과정을 대신 처리해 주기 때문에 러닝 커브가 낮다는 것이 큰 장점인데요. 그러다 보니 지나치게 프레임워크에 의존하여 안전하지 않은 코드를 무심코 작성하게 되는 경우가 종종 있는 것 같습니다. 이번 글에서는 독자분들께 통상 효과적인 처리 방식이라 생각하는 스프링 프레임워크를 통한 트랜잭션 처리가 잠재적으로 가져올 수 있는 위험성을 소개하고 살펴보겠습니다. 또한, 이 문제를 해결하기 위한 방법으로 최근 스프링과 조합되어 사용되는 비중이 점차 증가하고 있는 코틀린(Kotlin)을 활용하여 잠재된 위험성을 줄이는 방식 또한 알아보도록 하겠습니다. 프로그램의 효과 현대의 프로그램은 격리된 환경에서 단독으로 실행되는 것이 아니라 사용자의 다양한 요구사항에 맞추어 데이터베이스에서 정보를 읽거나, 다른 프로그램과 HTTP 프로토콜을 사용한 통신으로 데이터를 주고받습니다. 이렇게 프로그램이 외부 세계와 상호 작용하는 것을 통칭 ‘효과’라고 부릅니다. 아래 코드 예시와 같이, 전통적인 프로그램은 외부 세계와 상호 작용을 처리하는 컨텍스트(Context)를 함수에 직접 전달해야 했습니다. fun saveInDB(database: DatabaseContext, user: User) { database.begin() database.saveUser(user) database.commit() } 이러한 방식은 함수가 어떤 효과를 수행할 것인지 명시적으로 표현할 수 있지만, 개발자가 모든 과정을 하나하나 처리해야 한다는 단점이 있습니다. 예를 들어 데이터베이스에 데이터를 저장하려면 트랜잭션을 시작하고, 데이터를 저장하고, 트랜잭션을 커밋하는 과정을 모두 직접 처리해야 합니다. 게다가 코드의 실행 과정에서 예외가 발생할 수도 있으므로 이를 대비한 방어 로직 또한 작성해야 합니다. 최근에는 개발자가 이러한 과정을 직접 구현하기보다는 프레임워크에게 처리를 일임하는 것이 각광받고 있습니다. 특히 백엔드 개발에서 주로 사용하는 스프링은 이 효과를 손쉽게 처리할 수 있도록 DI와 AOP를 지원합니다. 아래 예시와 같이, 스프링 프레임워크에선 함수에 @Transactional 어노테이션을 추가하기만 하면 트랜잭션의 모든 과정을 스프링이 대신 처리하도록 구현이 가능합니다. AOP를 통한 효과를 처리하는 방법은 굉장히 유용하기는 하지만, 아쉬운 부분도 있습니다. 바로 모든 클래스와 함수에 어노테이션을 아무런 제약이 없이 설정할 수 있다는 점입니다. 이러한 유연성은 엉뚱한 곳에 효과를 부여하거나 반대로 반드시 필요한 곳에는 효과를 부여하지 못하는 부작용(Side effect)을 가져올 수 있습니다. @Transactional fun save(user: User) { userRepository.save(user) } 그렇다면 효과를 안전하게 처리하려면 어떻게 해야 할까요? 전통적인 방식에서는 효과 처리에 지나친 자율성이 부여되었기 때문에 작성된 코드의 동작을 예측하기 어렵게 만들었습니다. 따라서, 이러한 생각을 스프링 프레임워크에서도 도입하여 자율성을 제약한다면, 코드의 동작을 보다 예측 가능하도록 변경할 수 있을 것입니다. 지금부터는 코틀린을 활용하여 안전하게 효과를 처리하는 방법을 알아보도록 하겠습니다. 확장 함수로 효과 처리하기 코틀린에서 제약 조건을 설정하는 방법은 바로 확장 함수(Extension function)를 정의하는 것입니다. 확장 함수는 함수의 이름 앞에 수신 객체 타입을 지정하여 정의할 수 있습니다. 확장 함수는 이름처럼 수신 객체 타입에 기능을 추가하는 용도로도 쓰이지만, 반대로 생각하면 확장 함수를 해당 수신 객체가 있을 때만 사용할 수 있도록 제약하는 방식으로도 사용할 수도 있습니다. interface DatabaseContext {...} class PrimaryDatabaseContext: DatabaseContext {...} class SecondaryDatabaseContext: DatabaseContext {...} fun DatabaseContext.save(user: User) { // user를 데이터베이스에 저장 } save 함수에 DatabaseContext라는 수신 객체 타입을 설정하면 컨텍스트 없이는 단독으로는 save 함수를 호출할 수 없습니다. 스프링 코드에 확장 함수 적용하기 이제 코틀린의 확장 함수를 사용해서 스프링 예제 코드를 개선해 보도록 하겠습니다. 코드의 요구사항은 아래와 같습니다. 요구 사항 상품을 삭제하는 프로그램을 개발한다. 상품을 삭제하는 함수는 반드시 트랜잭션 범위 안에서만 호출해야 한다. 스프링에서는 함수를 트랜잭션 범위 안에서만 호출할 수 있도록 만들고 싶다면 아래와 같이 @Transactional 어노테이션을 추가하고 MANDATORY 옵션을 설정하기만 하면 됩니다. @Component class ProductComponent { @Transactional(propagation = Propagation.MANDATORY) fun delete(id: Long) { // 상품을 삭제 } } 그런데 만약 @Transactional 어노테이션이 누락된 채로 코드가 작성되었다면 어떤 위험이 있을까요? AOP 방식의 트랜잭션은 컴파일 타임에는 호출 관계에 대한 문법 에러를 감지할 방법이 없습니다. 따라서 런타임에 함수를 호출하게 되면 서버에서 에러가 발생하게 됩니다. 그렇다면 이 문제를 해결할 수 있는 근본적인 해결책은 무엇일까요? 가장 좋은 방법은 요구 사항 그대로 트랜잭션이 없는 경우, 컴파일 타임에 에러가 감지되도록 하여 개발자의 실수를 방지하는 것입니다. 이때, 아래 예시와 같이 코틀린을 사용한다면 delete 함수에 트랜잭션을 처리해 줄 특별한 컨텍스트를 지정하여 이 목표를 쉽게 달성할 수 있습니다. @Component class ProductComponent { fun DatabaseContext.delete(id: Long) { // 상품을 삭제 } } @Autowired lateinit var productComponent: ProductComponent fun doSomething(id: Long) { val databaseContext = PrimaryDatabaseContext() with(databaseContext) { with(productComponent) { delete(id) } } } 개선된 코드에서는 delete 함수에 @Transactional을 지정하는 대신에 DatabaseContext를 수신 객체 타입으로 지정하였습니다. 지금부터는 확장 함수의 특성으로 인해 DatabaseContext와 ProductComponent가 모두 주입된 영역에서만 delete 함수를 호출할 수 있습니다. 즉, 잘못된 코드를 억지로 사용하고 싶어도 컴파일 과정에서 문법 에러가 감지되어 컴파일이 실패하게 됩니다. 이는 기존에는 런타임 시점에서야 감지할 수 있었던 에러가 원천적으로 차단될 수 있다는 것을 시사하며, 효과를 안전하게 처리하는 방식에 한 발짝 가까워졌음을 의미합니다. 고차 함수로 효과 함수 전달 코틀린은 함수를 인자로 받거나 리턴할 수 있는 고차 함수를 지원합니다. 코틀린의 고차 함수를 적용한 예시는 아래와 같습니다. @Component class ProductComponent { fun delete(id: Long): DatabaseContext.() -> Unit { return { // delete product } } } @Autowired lateinit var productComponent: ProductComponent fun doSomething(id: Long) { val databaseContext = PrimaryDatabaseContext() val block = productComponent.delete(id) with(databaseContext) { block() } } 기존에 살펴보았던 delete 함수는 이제 상품을 직접 삭제하는 것이 아니라, 상품을 삭제하는 함수를 리턴하는 고차 함수로 작성되었습니다. 물론 리턴되는 함수에도 DatabaseContext가 수신 객체 타입으로 지정할 수 있기 때문에 제약 사항도 잘 지켜지고 있습니다. 효과 함수 리턴 방식의 장점은 컴포넌트로부터 효과를 독립적으로 분리할 수 있다는 데 있습니다. 이에 대한 증거로 delete 함수를 아무리 많이 호출하더라도 직접적으로 데이터베이스에는 영향을 줄 수 없다는 점을 들 수 있겠습니다. 즉, delete 함수로부터 전달된 효과 함수를 호출해야 비로소 데이터베이스에 저장된 상품을 삭제하는 효과가 발생합니다. 따라서, 이러한 고차 함수로 효과를 전달하는 방식을 도입하면 효과가 실행될 위치를 보다 유연하게 선택할 수 있습니다. 효과 처리 구현 지금까지 살펴본 방법들을 통해 효과를 안전하게 처리하는 방법에 한 발자국씩 가까워지고 있지만, 아직은 함수를 호출할 수 있는 위치를 제약했을 뿐이며 효과를 직접적으로 처리하는 컨텍스트가 수행하는 일이 없는 상태입니다. 이제 효과를 처리하고 싶다면, 아래 예시와 같이 컨텍스트가 효과를 처리할 수 있도록 함수를 추가해야 합니다. interface DatabaseContext { fun transactional(block: DatabaseContext.() -> T): T } class PrimaryDatabaseContext : DatabaseContext { override fun transactional(block: DatabaseContext.() -> T): T { // begin transaction val result = block() // commit return result } } @Autowired lateinit var productComponent: ProductComponent fun doSomething(id: Long) { val databaseContext = PrimaryDatabaseContext() val block = productComponent.delete(id) databaseContext.transactional { block() } } 예제에서는 DatabaseContext에 트랜잭션을 처리할 수 있는 함수 transactional을 추가했으며, 이 transactional 함수 구현부의 주석 부분은 트랜잭션의 처리 과정을 나타내고 있습니다. 지금부터는 이전 예시들의 doSomething 함수에서 사용되었던 with 함수로 컨텍스트를 주입하는 방식이 아닌, transactional 블록 안에서 block 함수를 호출하도록 변경할 수 있습니다. DSL로 Context 주입하기 DSL로 중복 코드 제거 추가적으로 개선할 부분은 반복되는 코드 작성을 줄이는 일입니다. DatabaseContext를 사용하기 위해서는 PrimaryDatabaseContext와 같은 구현체를 생성하여 주입하는 코드를 매 번 작성해야 합니다. 이와 같이 중복되는 코드는 전용 DSL(Domain Specific Language)을 만들어두면 보다 안전하고 가독성 높은 코드를 작성할 수 있습니다. fun transactional(block: DatabaseContext.() -> T) { val databaseContext = PrimaryDatabaseContext() databaseContext.transactional(block) } @Autowired lateinit var productComponent: ProductComponent fun doSomething(id: Long) { val block = productComponent.delete(id) transactional { block() } } DSL을 잘 구현하기 위해서는 함수의 기능을 잘 표현하는 이름을 지어주고 기능을 똑같이 구현해 주면 됩니다. 지금부터는 컨텍스트 주입을 위한 코드를 매번 작성할 필요가 없습니다. 단지, 효과를 대신 안전하게 처리해 줄 transactional 함수의 블록 안에 원하는 효과를 전달하기만 하면 됩니다. Scope를 통한 Context 주입 마지막으로 코드에는 개선할 부분이 한 가지 남아있습니다. transactional 함수는 아직 아무런 제약이 없기 때문에 코드 어디서든지 이 함수를 호출할 수 있습니다. 이를 방지하고 싶다면 transactional 함수 역시 확장 함수로 구현하면 될 것입니다. interface TransactionalScope fun TransactionalScope.transactional( block: DatabaseContext.() -> T ) { val databaseContext = PrimaryDatabaseContext() databaseContext.transactional(block) } @Service class ProductService: TransactionalScope { @Autowired lateinit var productComponent: ProductComponent fun doSomething(id: Long) { val block = productComponent.delete(id) transactional { block() } } } 확장 함수 구현을 위해, 위의 코드 예시와 같이 TransactionalScope 인터페이스를 선언하고 이를 transcational 함수의 수신 객체 타입으로 지정했습니다. 이에 따라 TranscationalScope가 지정된 영역에서만 transactional 함수를 호출할 수 있게 되었습니다. 실사용 사례: 코루틴(Coroutine) 이제 앞서 살펴본 예시들이 적용된 공식 라이브러리인 코루틴(Coroutine)을 소개드리며, 프로그램이 안전한 효과를 달성할 수 있도록, 소개드린 전략들이 적용된 곳이 많다는 점을 강조하고자 합니다. 스코프 및 컨텍스트 구조는 코틀린의 비동기 프로그래밍을 지원하는 코루틴 공식 라이브러리에서 쉽게 찾아볼 수 있습니다. 코루틴은 비동기 작업이라는 효과를 안전하게 처리하기 위해, 코루틴을 시작하고 중단하는 함수는 반드시 코루틴 스코프(CoroutineScope) 안에서만 호출하여 사용할 수 있도록 강제합니다. public fun CoroutineScope.launch(): Job // 코루틴 빌더 함수 public suspend fun delay(timeMillis: Long) // 중단 함수 fun main() = runBlocking { // CoroutineScope를 제공하는 함수 launch { // 새로운 코루틴 시작 delay(1000) // 중단 함수 } } 코루틴 라이브러리에서 정의한 대표적인 launch 코루틴 빌더와 delay 중단 함수를 살펴보면 수신 객체 타입으로 코루틴 스코프가 설정되어 있거나 이와 동일한 효과를 가져오는 ‘suspend’ 키워드가 함수에 설정된 것을 확인할 수 있습니다. 따라서, launch와 delay는 runBlocking 함수를 통해 제공된 스코프 안에서만 사용할 수 있도록 제한됩니다. 코루틴 스코프에 전달된 비동기 작업(Job)은 코루틴 스코프 안에 자동으로 주입되어 숨겨진 CoroutineContext를 통해 안전하게 처리됩니다. 아래 코드와 같이 CoroutineScope 인터페이스에는 CoroutineContext가 프로퍼티로 정의되어 있으며, 코루틴은 이에 대한 BlockingCoroutine 및 GlobalScope와 같은 다양한 스코프 구현체들을 제공합니다. public interface CoroutineScope { public val coroutineContext: CoroutineContext } 마치며 본 글에서는 하나하나 단계를 거치며 효과의 위험성을 확인하고, 문제를 해결하기 위해 코틀린을 활용한 안전한 효과 처리 방법에 대해 고민해 보았습니다. 이러한 고민과 과정을 통해, 예측 가능한 영역(Scope)에 컨텍스트(Context)를 주입하여, 요구 사항에 맞는 효과가 안전하게 처리될 수 있도록 구현하는 방법을 소개드릴 수 있었습니다. 보다 안전한 코드를 작성하기를 원하시는 독자분들에게 많은 도움이 되기를 바라며 끝까지 읽어주셔서 감사합니다. written by Alan. an edited by June.6
KCC2024 “Kakao Tech Workshop”에서 만나요!
kakao tech · 3달 전
올여름에도 한국컴퓨터종합학술대회(KCC)에 카카오가 후원사로 함께합니다. 이번 학회에 참석하려고 계획하시는 분들께 카카오테크워크숍 소식을 전해드립니다. KCC2024 학회 정보 행사명: KCC 2024 (2024 한국컴퓨터종합학술대회) 주최/주관: 한국정보과학회 일정: 2024.06.26.(수) ~ 06.28.(금) 장소: ICC 제주 워크숍 초대의 말씀 KCC 2024 Kakao Tech Workshop에서는 카카오의 추천 기술, 대용량 데이터 처리 기술, 클린 플랫폼 및 스팸 필터링 기술 등을 공유합니다. 카카오의 개발진과 연구진이 직접 준비한 이번 워크숍에서는, 실 서비스 적용시 발생하는 이슈들과 이를 해결하는 전략을 다양한 사례들을 나누고자 합니다. 카카오의 기술이 다양한 서비스와 비즈니스를 어떻게 만들어가는지 다양한 사례를 발표할 이번 행사에 여러분을 초대합니다. 워크숍 일시 2024.06.26.(수) 13:55 ~ 17:55 프로그램 시간 발표 제목 발표자 13:55-14:00 오프닝 조훈(hunter) Part 1. 좋은 추천을 위한 카카오의 노력 14:00-14:30 카카오의 실시간 광고 추천 서빙: 버킷 시스템을 활용한 다중 머신러닝 모델 실험 지원 장주빈(jb), 유종현(faust) 14:30-15:00 광고추천에서 다량의 배치 추론 모델을 안정적으로 스케줄링하는 방법 김재형(sam), 김대현(daniel) 15:00-15:30 리타게팅 광고 추천 서빙에 필요한 데이터 파이프라인 개발기 이상후(moses) 문창주(pooh) 15:30-16:00 추천 시스템의 편향을 고려한 모델링 김동규(francis) 정성엽(rick) 16:00-16:30 쉬는 시간 및 자유로운 Q&A Part 2. 클린 플랫폼을 만드는 카카오의 노력 16:30-17:00 카카오에서 스팸(성인) 이미지 대응을 하기 위해 시도해 오던 것들 오창화(herschel) 17:00-17:30 카카오의 스팸 콘텐츠(text)를 대응하기 위한 LLM 적용기 조혜연(zoey) 17:30 - 마무리 및 자유로운 Q&A 조훈(hunter) ( 학회 홈페이지에서 보기 ) 본 행사는 KCC 2024 등록자만 참여할 수 있고, 무료 워크숍입니다. 6월 26일, 제주에서 만나요 🙋♀️ (관련 글) 작년 KCC 카카오테크워크숍의 기록들 👀 KCC 2023 후기 (1) 학회 스케치와 참여 소감 KCC 2023 후기 (2) 카카오 크루들과 함께
구름톤유니브 2기 벚꽃톤! 현장 스케치
kakao tech · 4달 전
봄이 시작하던 3월, 카카오 AI 캠퍼스에서 진행한 구름톤 유니브 2기 벚꽃톤 행사 현장의 설렘과 열정을 공유합니다! 🌸🔥 kakao x goorm 9oormthon univ
제6회 Kakao Tech Meet에 초대합니다!
kakao tech · 4달 전
Kakao Tech Meet #6 Kakao Tech Meet은 작년부터 이어온 카카오의 공개 기술 세미나입니다. 올해에도 계속 최신 기술 트렌드와 경험 및 노하우를 자주, 지속적으로 공유하며 개발자 여러분과 함께 성장을 도모하고 긴밀한 네트워크를 형성하고자 합니다. 여섯 번째 Kakao Tech Meet에서는 클린 플랫폼을 만드는 기술을 주제로 스팸 이미지, 스팸 텍스트, 스팸 메일을 대응하기 위한 경험과 노하우를 공유합니다. 또한 발표에서 못다 한 질문과 이야기를 나눌 수 있도록 패널토의를 진행합니다. 판교역에 연결된 카카오 아지트에서 열리는 이번 기술 세미나에 많은 관심과 참여 부탁드립니다. 앞으로도 카카오는 개발자 커뮤니티의 일원으로서, 개발자 커뮤니티와 함께 성장하기 위해서 꾸준히 노력하겠습니다. 일시 6월 13일(목) 19:00 - 21:30 (18:30부터 입장 가능합니다) 장소 카카오판교아지트, 4층 스위치온2 / 신분당선 판교역 4번 출구 (경기 성남시 분당구 판교역로 166) 오프라인 참석자에게는 행사장 입장을 위한 ‘웰컴패스’를 카카오톡으로 보내 드립니다. festa에 등록한 휴대폰 번호가 정확한지 확인해 주세요. 온라인 라이브 스트리밍을 제공하며, 접속 정보는 행사 전 안내 드릴 예정입니다. 발표 내용 & 발표자 소개 (1) 카카오의 스팸 메일 대응 전략: 문자열 변형 CASE STUDY 스팸 메일은 다양한 형태로 존재합니다. 그중 우리는 스팸 검사를 회피하기 위해 의도적으로 문자열을 깨뜨려 들어오는 경우를 집중적으로 살펴보면서, 문자 인코딩 등의 이야기를 나누며 어떻게 대응했는지 경험을 공유합니다. 카카오가 어떻게 스팸 메일 필터링을 수행하고 있는지에 대해 궁금한 분들에게 추천합니다. #MAIL #Text #Encoding 발표자: 이소민(laura.0326) 클린플랫폼에서 스팸 메일 필터링 업무를 맡고 있습니다. 다양한 것을 경험하는 걸 좋아합니다. (2) 이미지 기반 스팸 대응을 위한 카카오의 AI 기술 활용 스팸 이미지 분류를 하기 위해 시도했던 다양한 경험을 공유합니다. 2018년부터 스팸성 이미지 대응 업무에 ML/DL을 도입해온 과정에서, 다양한 문제를 만나고 이를 해결해 가는 과정에서 쌓은 경험을 공유합니다. 다양한 업무 영역에서 ML/DL 도입을 위해 고민하는 개발자에게 추천합니다. #AI #ML #DL 발표자: 오창화(herschel.alway) 어쩌다 보니 세월의 흐름에 이끌려 백엔드 개발에서 스팸 관련한 AI 연구도 같이 하고 있는 개발자입니다. (3) 스팸 콘텐츠 대응을 위한 카카오의 대규모 언어 모델(LLM) 도입 사례 문자(text) 스팸 콘텐츠를 효과적으로 분류하기 위해 LLM을 활용한 경험을 소개합니다. 기존의 규칙 기반(Rule-Base) 스팸 콘텐츠에 LLM을 적용한 배경을 설명합니다. 스팸을 단순히 분류하는 것을 넘어, 스팸으로 판정된 이유를 설명하는 LLM의 데이터 수집 방법을 소개합니다. 특히, ‘스팸’ 분야에서 LLM을 어떤 방식으로 활용할 수 있는지에 대한 실전 경험을 중점적으로 다룹니다. 스팸 탐지에 관심 있는 누구나, 특정 도메인에 LLM을 적용해 보고 싶은 개발자에게 추천합니다. #AI #LLM #SpamDetection #TextClassification #TextGeneration 목차 스팸 분류를 위한 LLM 도입 배경 스팸 도메인 LLM 데이터 수집 과정 중 챌린지와 해결 방법 발표자: 조혜연(zoey.fully) 카카오의 다양한 서비스로 유입되는 콘텐츠에서 스팸을 분류하는 업무를 맡고 있습니다. AI 기반의 스팸 분류 기술을 연구하고 있습니다. ★ 안내 공간 제약상, 오프라인 참여를 신청해 주신 분들 중 일부만 초대드리는 점 양해 부탁드립니다. 행사 안내는 카카오톡으로 제공됩니다. (등록하신 휴대폰 번호가 정확하지 않거나 카카오톡과 연결되지 않은 경우, 안내드리기 어렵습니다.) 오프라인 참석이 선정된 분들께는 6/5(수) 이후, 카카오톡으로 안내드릴 예정입니다. 온라인 라이브를 통해 모든 분들이 참여하실 수 있습니다. 행사 전 접속 정보를 안내드릴 예정입니다. 발표 세션의 녹화 영상은 행사 종료 후 제공드릴 예정입니다(유튜브 @kakaotech 및 기술 블로그 tech.kakao.com). 발표자료는 제공되지 않습니다. 간단한 다과와 기념품을 드립니다. 식사는 별도로 제공하지 않습니다. 문의 tech@kakaocorp.com 신청하러 가기💌 https://festa.io/events/5243
Golang GC 튜닝 가이드
yanoo.kim · 4달 전
Golang으로 프로그램을 개발하다 보면, 어느 순간 GC(Garbage Collector)가 성능의 짐이 됩니다. 알아서 메모리를 관리해 줬던 고마운 GC가, CPU 자원 소모량을 점차 늘려가며 로직에 부담을 주게 되는 것입니다. 이제부터는 인터넷 여기저기를 찾아가며 Garbage Collecting 작업을 효과적으로 수행하기 위한 튜닝을 궁리해야 할 시간입니다. 이 글은 이러한 문제를 해결하기 위한 실질적인 Golang GC 튜닝 방법을 소개하는 글입니다. 독자 여러분께 인터넷 여기저기서 찾고 배운 튜닝 방법들을 정리하여 전달하는 한편, 잘 언급되지 않지만 크게 도움받은 부분들을 전달하는 데 내용을 집중하였습니다. 따라서 검색 가능한 다른 글들에서 이미 자주 언급된 내용과 Golang의 GC가 동작하는 방식은 이 글에서 다루지 않습니다. 물론 이 주제들 또한 Golang GC를 튜닝하기 위해 이해하면 도움이 되는 내용이기 때문에, 필요한 경우 글 말미의 레퍼런스를 참고해 주시면 되겠습니다. [1] 1. 모든 Golang 튜닝의 핵심은 Profile이다 Golang GC 튜닝에 앞서 정말 튜닝이 필요한 상황인지 먼저 확인해야 합니다. 튜닝할 필요도 없는데 괜히 머리를 싸매고 시간을 보내는 것은 낭비일 수 있습니다. 이때, 튜닝이 필요한지 또는 아닌지를 판단할 수 있는 핵심 도구가 바로 Profile입니다. 우리는 Profiling을 통해 프로그램 내 성능에 악영향을 주는 여러 원인들뿐만 아니라, GC가 주는 악영향까지 확인할 수 있습니다. Golang의 Profiling 방법은 여러 가지가 있습니다. 이 중 Golang의 공식 블로그에서는 아래와 같이 프로그램에서 직접 Profile 결과를 파일로 저장하는 방법과 웹으로 Profiling을 요청받아 응답하는 방법 두 가지를 소개하고 있습니다. import ( "os" "runtime/pprof" ) … f, err := os.Create(“cpu.profile”) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() … import ( "http" _ "net/http/pprof" ... ) func main() { ... go func() { http.ListenAndServe("0.0.0.0:6060", nil) }() ... } 저는 필요할 때 제 단말기에서 바로 Profiling 결과를 확인할 수 있는 웹 방식을 선호합니다(물론 인프라가 갖춰져 있으시다면, Pyroscope 도구를 통한 Continuous Profiling을 추천드립니다). 이제 웹 방식을 통해 구동된 웹서버에 명령어를 사용해 Profiling을 요청을 하면, 필요할 때 언제든지 최신의 성능 측정 결과를 응답받을 수 있습니다. 명령어 예시들은 아래와 같습니다. go tool pprof -http=0.0.0.0:8080 http://…..:6060/debug/pprof/profile wget -O cpu.pprof http://…..:6060/debug/pprof/profile go tool pprof -http=0.0.0.0:8080 cpu.pprof 명령어를 실행하면 프로파일링을 위해 30초 정도의 시간이 소요된 후(소요시간 30초는 변경할 수 있습니다), 웹브라우저 상에서 프로파일링 한 결과를 확인할 수 있습니다. 이때, 본인의 단말에서 프로파일링 명령어를 실행한 경우 웹브라우저가 자동으로 실행되며 결과를 확인할 수 있습니다. 또한, 서버에서 명령어를 실행한 경우 외부 웹브라우저에서 서버의 주소를 통해 프로파일링 결과에 접근할 수 있습니다. 이후 프로파일링 결과 화면에서 상위항목(Top)으로 목록을 정렬하면 CPU를 많이 점유하는 함수들을 순서대로 확인할 수 있습니다. 이때 아래와 같이 Golang의 GC가 사용하는 함수인 runtime.findObject 또는 runtime.greyObject가 목록의 최상위권에 보이는 경우, 마침내 GC 튜닝을 해야 할 시점이 된 것을 알 수 있습니다. [2] Showing nodes accounting for 362.50s, 69.04% of 525.02s total Dropped 1758 nodes (cum 2) GOGC 값이 큰 경우 OutOfMemory(이하 OOM) 발생 가능성이 커집니다. 기존 Heap 사용량이 40GB이고 GOGC가 50이라면, 1.5배 상승한 60GB가 되어야 GC가 수행되기 때문입니다. 이를 극복하기 위해 프로그램이 시작되자마자 더미 메모리를 할당하는 ballast[4] 기법도 존재합니다. 하지만 GOMEMLIMIT이라는 멋진 GC 튜닝 해결책이 나왔기에, ballast는 잊으셔도 됩니다. GOMEMLIMIT은 프로그램이 사용할 수 있는 메모리 사용량 한계선을 정하는 설정입니다. 이 방식에서는 설정한 GOMEMLIMIT의 값만큼 메모리 사용량이 올라가는 경우에만 GC가 수행됩니다. 따라서 프로그램이 사용할 수 있는 최대 메모리 한계선을 미리 산정한 후, 그 값보다 작은 값을 GOMEMLIMIT으로 설정하면 손쉽게 GC를 튜닝할 수 있습니다. 한계선보다 작은 값을 설정하는 이유는 GOMEMLIMIT은 SoftLimit으로 프로그램이 설정된 GOMEMLIMIT 보다 조금 더 많은 메모리를 사용할 수 있기 때문입니다. 이로써 GC가 항상 최대한 늦게 동작할 수 있는 환경이 갖추어지며, GC Cycle이 최대로 길어지게 되어 STW가 최소화됩니다. 3. Heap Profile의 alloc_space와 alloc_objects가 높은 값을 튜닝하라 앞서 ‘모든 Golang 튜닝의 핵심은 Profile이다’ 문단에서 언급한 Profiling을 Heap 메모리에도 적용할 수 있습니다. Golang의 메모리 영역은 크게 Stack과 Heap으로 구성되어 있고, 이 중 Stack 메모리는 GC가 없어도 함수의 호출과 종료에 따라 자연스럽게 관리됩니다. 반대로 Heap 메모리는 GC의 관리가 필요한 객체들이 있는 메모리 영역으로, 바로 이 Heap 영역이 GC 튜닝의 핵심이라고 말씀드릴 수 있습니다. [5] Heap Profile은 Heap 메모리를 차지하고 있는 객체를 아래와 같은 네 가지의 지표를 기준으로 분석합니다. inuse_objects: 현재 사용 중인 객체의 수 inuse_space: 현재 사용 중인 메모리 양 alloc_objects: 프로그램이 시작된 후 할당된 총 객체 수 alloc_space: 프로그램이 시작된 후 할당된 총 메모리 양 GC 튜닝의 관점에서, inuse 타입 지표와 alloc 타입 지표 중 무엇이 더 중요할까요? 이 중에서는 alloc 타입이 더 중요한 지표입니다. 예시로 inuse가 1GB인데 alloc이 1GB인 객체는, 처음 한 번 메모리에 할당된 후 계속해서 사용되고 있다는 의미입니다. 즉, GC가 개입할 필요가 없기에 CPU 효율적이죠. 반대로 inuse가 1GB인데 alloc이 1TB인 객체는, Heap 메모리를 객체에 1024번 할당했다는 뜻입니다. 이는 GC도 1024번 일어났다는 뜻이며, 그만큼 CPU를 사용했다는 의미가 되겠습니다. 그렇다면 alloc_space와 alloc_objects 지표들은 어떠할까요? 이 경우 둘 모두 중요합니다. alloc_space가 크다면, 프로그램 동작 과정에서 사용되는 Heap 메모리가 빠르게 증가한다는 것을 의미합니다. 이에 따라 GC Cycle이 빨라지며, GC가 자주 수행됩니다. 성능을 개선하려면 이를 튜닝하여 GC Cycle을 다시 늦춰 GC가 수행되는 주기를 길게 바꿔야죠. 아울러 alloc_objects가 크다면, GC 과정에서 일어나는 Marking 작업 등에서 CPU 자원을 소모하게 됩니다. 즉, 큰 alloc_space 값은 GC의 주기를 짧아지게 할 뿐만 아니라 GC의 수행시간 자체도 오래 걸리게 되는 것입니다. 결과적으로 프로그램의 성능에 영향을 미치는 STW 시간도 늘어나게 됩니다. 4. Flame graph로 튜닝 포인트 찾기 Golang의 Profiling은 어떤 함수가 리소스를 많이 소모했는지를 Flame graph로 표현합니다. 아래와 같이 alloc_objects와 alloc_space 지표를 기준으로 Flame graph를 표시했을 때, 특정 함수의 가로축 길이가 길어 전체에서 차지하는 비중이 긴 경우, 해당 함수가 프로그램 동작 과정에서 GC의 개입이 필요한 객체들을 많이 생성하고 있다는 것을 알 수 있습니다. 확인이 필요한 함수를 클릭하면 아래와 같이 원본 소스코드를 바로 확인할 수 있고, 함수 내에서 어떤 코드가 객체를 많이 할당하고 있는지도 확인할 수 있습니다. 5. -gcflags=‘-m -m’을 통해, 무엇이 왜 Heap 메모리로 객체가 할당되었는지 확인하기 이제 무엇을 튜닝해야 할지와 소스코드의 위치는 찾았습니다. 하지만 어떻게 튜닝해야 할지는 아직 방법을 모르는 상태입니다. 왜 특정 개체가 GC가 수행되어야 하는 Heap 메모리 영역에 할당되었는지를 알아야, 프로그램 전체적으로 GC가 필요한 객체들을 줄이고 튜닝할 수 있는 것입니다. 그때 사용할 수 있는 Golang의 빌드(Build) 옵션이 바로 gcflags입니다. [6] gcflags에 대해 설명하기 위해, 먼저 아래와 같은 소스코드를 예시로 사용하겠습니다. package main import ( "fmt" ) func main() { i := 0 j := i + 1 k := j + 1 fmt.Println(j) fmt.Printf("%d\n",k) } 이 소스코드의 빌드 시 아래와 같이 gcflags 옵션을 사용할 수 있습니다. % go build -gcflags='-m' main.go # command-line-arguments … ./main.go:13:17: j escapes to heap ./main.go:14:15: ... argument does not escape ./main.go:14:23: k escapes to heap 적용한 gcflags 옵션을 통해, j와 k 객체는 Heap 메모리에 할당되었고, i 객체는 Heap에 할당되지 않은 것을 알 수 있습니다. 이 중 Heap 메모리에 할당된 객체는 GC의 관리 대상이 되는데, 이러한 객체들을 줄이는 것이 GC 튜닝의 목표이며, gcflags 옵션으로 그 대상들을 정확히 확인할 수 있습니다(j와 k가 왜 Heap 영역에 할당되었는지는 이어서 다시 설명하겠습니다). 또한, gcflags 옵션에 ‘-m -m’을 인자로 전달하면, 그 원인도 살필 수 있습니다. 변수가 Heap 메모리에 할당되는 데는 다양한 이유가 있으며, 원인을 참고하여 최대한 많은 객체를 Stack 메모리 영역으로 할당해야 합니다. % go build -gcflags='-m -m' main.go # command-line-arguments … ./main.go:14:23: k escapes to heap: ./main.go:14:23: flow: {storage for ... argument} = &{storage for k}: ./main.go:14:23: from k (spill) at ./main.go:14:23 ./main.go:14:23: from ... argument (slice-literal-element) at ./main.go:14:15 ./main.go:14:23: flow: fmt.a = &{storage for ... argument}: ./main.go:14:23: from ... argument (spill) at ./main.go:14:15 ./main.go:14:23: from fmt.format, fmt.a := "%d\n", ... argument (assign-pair) at ./main.go:14:15 ./main.go:14:23: flow: {heap} = *fmt.a: ./main.go:14:23: from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./main.go:14:15 ./main.go:13:17: j escapes to heap: ./main.go:13:17: flow: {storage for ... argument} = &{storage for j}: ./main.go:13:17: from j (spill) at ./main.go:13:17 ./main.go:13:17: from ... argument (slice-literal-element) at ./main.go:13:16 ./main.go:13:17: flow: fmt.a = &{storage for ... argument}: ./main.go:13:17: from ... argument (spill) at ./main.go:13:16 ./main.go:13:17: from fmt.a := ... argument (assign-pair) at ./main.go:13:16 ./main.go:13:17: flow: {heap} = *fmt.a: ./main.go:13:17: from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:13:16 ./main.go:13:16: ... argument does not escape ./main.go:13:17: j escapes to heap ./main.go:14:15: ... argument does not escape ./main.go:14:23: k escapes to heap 6. GC 튜닝은 Benchmark를 중심으로 확인하자 하지만 gcflags 옵션으로 일일이 escapes to heap을 확인하는 것은 눈이 아픈 일입니다. 그리고 코드상에 escapes to heap이 많아도, 실제 발생하는 오브젝트(Object) 할당은 적을 수 있습니다. 한 번 할당한 오브젝트를 계속 재활용할 수도 있고, 실제로는 자주 호출되지 않는 코드일 수도 있기 때문이죠. 가장 확실한 건 실제 서비스에 배포 후 이를 확인하는 것이지만, 그러면 너무 피곤하고 괴로울 수 있습니다. 따라서 Benchmark를 중심으로 집중적으로 튜닝하는 게 좋습니다. Benchmark를 사용하는 예시 명령은 아래와 같습니다. % go test -bench=. -benchmem -benchtime=10s -cpuprofile=cpu.prof -memprofile=mem.prof -gcflags='-m' # golang/gctune [golang/gctune.test] ./gctune_test.go:15:6: can inline BenchmarkNoConstantCapacity ./gctune_test.go:23:6: can inline BenchmarkConstantCapacity ./gctune_test.go:15:34: b does not escape ./gctune_test.go:18:18: make([]int, n) escapes to heap ./gctune_test.go:23:32: b does not escape ./gctune_test.go:26:18: make([]int, n, 1024) does not escape # golang/gctune.test _testmain.go:39:6: can inline init.0 _testmain.go:47:24: inlining call to testing.MainStart _testmain.go:47:42: testdeps.TestDeps{} escapes to heap _testmain.go:47:24: &testing.M{...} escapes to heap goos: darwin goarch: arm64 pkg: golang/gctune BenchmarkNoConstantCapacity-10 42037057 271.2 ns/op 2048 B/op 1 allocs/op BenchmarkConstantCapacity-10 1000000000 0.6588 ns/op 0 B/op 0 allocs/op PASS 이때 사용된 옵션들을 설명하면 아래와 같습니다. -benchmem 한 번의 bench loop에서 오브젝트 할당이 얼마나 자주, 크게 할당했는지를 보여줍니다. 위 예제는 두 Benchmark의 결과를 보여주는데, 한 Benchmark는 루프를 한번 수행할 때마다 Heap 메모리에 2048 바이트의 객체를 하나 할당했고, 다른 하나는 루프를 수행할 동안 Heap 메모리에 어떠한 객체도 할당하지 않았음을 보여줍니다. -cpuprofile, -memprofile Bench가 동작하는 동안의 Profiling 결과를 파일로 저장하는 옵션입니다. 이를 통해 객체 할당이 어디서 일어났는지를 쉽게 확인할 수 있죠. -gcflags=’-m’ 위에서 언급한 이 빌드 옵션은 테스트 시에도 사용할 수 있습니다. 이를 통해 어디서 Heap 메모리로 할당이 일어났는지, 왜 일어났는지를 확인할 수 있습니다. 이렇게 Benchmark를 사용해 튜닝을 진행한 후에도 Heap 메모리에 할당된 오브젝트는 줄었는데, 오히려 Bench 상에서의 속도는 떨어지는 결과가 나올 수도 있습니다. 특히 Pool 같은 기법을 사용하면 객체가 재활용되며 Heap 영역의 할당은 줄지만, Pool에 대한 추가적인 연산들이 수행되며 CPU 자원을 소모하기 때문입니다. 또한, Heap 메모리 할당을 줄여서 얻는 이득은 GC가 발생해야만 그 효과를 확인할 수 있는데, Benchmark는 짧은 시간 동안만 실행되는 경우가 많아 GC 부하가 잘 드러나지 않기 때문입니다. 어느 것이 이득인지는 작성된 프로그램에 따라 다를 수 있기 때문에, 이것은 개발자의 판단이 필요한 부분입니다. 7. Heap 할당을 줄이는 팁들 이제부터 본격적으로 Heap 메모리 할당을 줄일 수 있는 팁들을 하나씩 소개드리겠습니다. 1) 비정형 인자는 Heap에 할당되니 조심해야 합니다. 아래 코드에서 j와 k 객체는 Heap 메모리에 할당되지만, i는 Stack 메모리에 할당되었습니다. 그 이유는 fmt.Println, fmt.Printf 함수 때문입니다. 두 함수는 any 타입의 인자를 사용하기 때문에 무엇이든 전달받을 수 있습니다.[7] 그리고 무엇이든 받기 위해, 각 변수에는 그 type을 설명하는 보조 정보가 필요하게 됩니다. 이때, 보조 정보가 있는 데이터는 반드시 Heap 메모리 영역에 할당되게 됩니다. 즉 any 만이 아니라, interface 및 reflect 등의 타입을 읽고 해석하는 추상화가 필요한 데이터들은 GC 부하를 일으키는 원인으로 작용합니다. [8] package main import ( "fmt" ) func main() { i := 0 j := i + 1 // j escapes to heap k := j + 1 // escapes to heap fmt.Println(j) fmt.Printf("%d\n",k) } 그렇다고 반드시 비정형 인자를 소스코드에 사용하지 말라는 의미는 아닙니다. 비정형 인자 또한 코드의 가독성과 생산성을 높여주는 좋은 장점들이 있습니다. 필요 이상으로 사용을 금하다가 가독성과 생산성을 떨어뜨리면, 배보다 배꼽이 크게 될 수 있습니다. 비정형 인자를 제거하는 방법은 GC 튜닝이 필요할 때, 혹은 프로그램의 성능이 문제다 싶은 상황이 오면 적용하는 것을 권장합니다. 추가로 강조드리고 싶은 점은, 비정형 인자로 사용될 때만 오브젝트가 Heap 메모리에 할당되는 것이 아니라, 비정형 인자로 사용될 가능성만 있어도 컴파일 시점에 Heap 영역으로 할당되도록 정해진다는 것입니다. 아래 코드를 예시로 들어 설명드리겠습니다. 예시에서 i가 1일 가능성은 없습니다. Golang 컴파일러가 똑똑하게 이를 Deadcode로 판단하면 좋으련만 그렇지 못했습니다. 또한, j와 k가 fmt의 함수들로 인해 any, 즉 비정형인자로 사용될 가능성이 있다고 판단하였고 선언 시점부터 j와 k를 Heap 메모리에 할당하였습니다. package main import ( "fmt" ) func main() { i := 0 j := i + 1 // j escapes to heap k := j + 1 // escapes to heap if i == 1 { fmt.Println(j) fmt.Printf("%d\n",k) } } 에러가 발생했을 때 예외적인 상황을 파악하기 위해 Logging을 하는 경우가 종종 있습니다. 반전은 이런 코드가 인자로 사용된 변수들을 항상 Heap 메모리 영역으로 할당하게끔 만들 수 있다는 것입니다. 드문 경우에 짧은 시간 동안 동작하는 코드지만 GC 대상이 늘어나는 비극적인 상황이죠. 따라서 Logger를 사용할 때도 비정형 인자를 피하는 것이 중요합니다. 2) type을 정해서 출력하는 Logger를 선택합니다. 비정형 인자를 가장 많이 사용하는 경우가 Logging입니다. Golang에는 여러 Logger들이 존재하는데, 이 중 무엇을 선택하느냐에 따라서 Heap 메모리 할당에 큰 차이를 보입니다. 선택할 수 있는 Logger의 종류에는 아래와 같이 log, slog 및 zap 등이 있습니다. import “log” func logtest() { int4log := 2 // escapes to heap slice4len := make([]int, 10, 1024) slice4log := make([]int, 10, 1024) // escapes to heap log.Println(slice4log, int4log) // slice4log & int4log escapes to heap log.Println(len(slice4len)) // len(slice4len) escape to heap } import “slog” import ( "log/slog" "os" ) func slogtest() { int4slog := 3 slice4len := make([]int, 10, 1024) slice4slog := make([]int, 10, 1024) // escapes to heap logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) logger.Info( // context.backgroundCtx() escapes to heap "slogtest", slog.Any("slice", slice4slog), // ~R0 escapes to heap slog.Int("int", int4slog), // ~R0, KindInt64 escapes to heap slog.Int("len",len(slice4len))) // ~R0, KindInt64 escapes to heap } import ( "go.uber.org/zap" ) func zaptest() { int4zaplogger := 4 slice4len := make([]int, 10, 1024) slice4zaplogger := make([]int, 10, 1024) // escapes to heap zaplogger, _ := zap.NewProduction() zaplogger.Info("zaplogtest", zap.Ints("ints", slice4zaplogger), // zap.ints(zap.nums) escapes to heap zap.Int("int", int4zaplogger), zap.Int("len", len(slice4len))) zaplogger.Sync() } 기본 패키지의 log는 fmt 패키지의 여러 함수들처럼 any의 사용비중이 높고, 이에 따라 당연히 인자들이 모두 Heap으로 할당됩니다. slog는 go1.21에서 추가된 신규 패키지인데요, 보기에는 type을 정하게 되어있어 비정형 인자가 아닐 거 같고, 그래서 Heap 할당이 없을 것처럼 생각됩니다. 하지만, 내부적으로 Value를 추상화하는 관점에서 any를 사용하였고, 이로 인해 Heap 메모리에 할당되어 버립니다. 즉, 애써 타입을 적었지만, fmt 패키지와 다를 바가 없는 것입니다. 하지만 Uber에서 만든 zap logger는 slog와 비슷한 모양을 가졌으면서도, Heap 메모리에 할당이 훨씬 적습니다. 일단 Primitive type들은 확실하게 Stack 메모리에만 할당되며 Heap 영역으로 Esacpe 하지 않습니다. 물론 예외적인 경우도 있지만, 그래도 다른 Logger보다는 훨씬 좋은 성능을 보여줍니다. 3) Pointer 변수는 무조건 Heap에 할당되니 조심해야 합니다. 보통 함수의 인자로 포인터를 즐겨 사용합니다. 값을 복사하는 것보다, 포인터로 넘기는 것이 CPU 효율적이라고 생각하기 때문인데요, Golang에서는 CallByPointer보다 CallByValue가 효율적인 경우가 자주 일어납니다. 왜냐하면 포인터로 선언되는 순간, 해당 오브젝트는 무조건 Heap 영역에 할당되기 때문입니다. [9] 4) slice를 생성 시 Capacity가 상수인 경우 Stack 메모리에 들어갈 수 있습니다. slice 또는 map을 생성(Make) 할 때, Capacity를 입력하는 방식은 필수적이면서도 유용한 팁입니다. slice나 map이 커져가면, 새롭게 메모리를 할당하고 해당 영역에 데이터를 다시 적재한 후 기존 객체는 버려버리는 realloc이 발생하는데, 이것을 피할 수 있기 때문입니다. 여기에 추가로, 가능하다면 Capacity(또는 len)을 상수로 입력한다면, 아예 Heap 메모리 할당을 없는 일로 만들 수 있습니다. 이는 Golang이 Data type을 고려해 크기가 64KB 이하인 객체의 경우 Stack 메모리에 선언하는 기능이 있기 때문입니다. [10] 이 방법은 놀랍도록 쉬우면서도 성능 개선에 많은 도움을 줄 수 있습니다. package main import ( "testing" ) var ( n = 256 ) const ( maxSize = 1024 ) func BenchmarkNoConstantCapacity(b *testing.B) { total := 0 for i := 0; i < b.N; i++ { s := make([]int, n) total += len(s) } } func BenchmarkConstantCapacity(b *testing.B) { total := 0 for i := 0; i < b.N; i++ { s := make([]int, n, maxSize) total += len(s) } } go test -v -bench=. -benchmem -gcflags="-m" ... ./go_test.go:18:18: make([]int, n) escapes to heap ... ./go_test.go:26:18: make([]int, n, maxSize) does not escape ... goos: darwin goarch: arm64 pkg: exam BenchmarkNoConstantCapacity BenchmarkNoConstantCapacity-10 4909958 233.0 ns/op 2048 B/op 1 allocs/op BenchmarkConstantCapacity BenchmarkConstantCapacity-10 1000000000 0.6222 ns/op 0 B/op 0 allocs/op PASS ok exam 2.348s 5) Pool은 Heap 메모리 할당을 줄일 수 있지만, CPU가 많이 소모될 수 있습니다. 이런저런 방법 모두 적용할 수 없어 Tuning이 어렵다면 가장 쉽게 적용할 수 있는 방법이 Pool입니다. 하지만 Pool을 관리하면서 오는 비용 때문에 오히려 CPU 리소스를 낭비할 수 있기 때문에, 무엇이 유리한지는 잘 선택해야 합니다. Pool을 적용한 예시 코드는 아래와 같습니다. package main import ( "fmt" "sync" "testing" ) var ( n = 256 baPool = sync.Pool{ New: func() any { res := make([]int, n) return &res }, } ) const ( maxSize = 1024 ) func BenchmarkNoCapacity(b *testing.B) { total := 0 for i := 0; i < b.N; i++ { s := make([]int, n) total += len(s) } } func BenchmarkPool(b *testing.B) { total := 0 for i := 0; i < b.N; i++ { s := baPool.Get().(*[]int) total += len(*s) baPool.Put(s) } } func BenchmarkConstantCapacity(b *testing.B) { total := 0 for i := 0; i < b.N; i++ { s := make([]int, n, maxSize) total += len(s) } } goos: darwin goarch: arm64 BenchmarkNoCapacity-10 13474758 265.0 ns/op 2048 B/op 1 a4llocs/op BenchmarkPool-10 416718634 8.661 ns/op 0 B/op 0 allocs/op BenchmarkConstantCapacity-10 21055032 171.7 ns/op 0 B/op 0 allocs/op PASS ok command-line-arguments 12.347s 6) 최신 패키지를 잘 살펴봅니다. 보통 Sort는 꽤 비싼 연산이지만, ‘적은 개수를 정렬하려는 거니 괜찮을 거야’라는 생각에 자주 사용하는 경우도 으레 있습니다. 이때 기본적으로 제공되는 sort.Slice가 많이 사용되는데요, 대신 slices.SortFunc를 사용하는 편이 GC 튜닝에 더 유용합니다. 왜냐하면 sort.Slice는 호출될 때마다 무조건 두 개의 객체를 Heap 메모리에 생성하고 할당하는데 반해, slices.Sort는 생성하는 객체 자체가 없기 때문이죠. 물론 Golang은 계속해서 새로운 버전이 출시되며 발전하고 있기 때문에, 언젠가는 sort.Slice도 최적화되어 Heap 영역 할당이 없도록 바뀔지도 모릅니다. 이러한 측면에서나 Golang에 추가된 새로운 기능, 새로운 GC 및 새로운 패키지를 즐기기 위해서라도 Golang의 신규 버전들을 모니터링하시기를 권장드립니다. slices.SortFunc와 sort.Slice에 대한 Benchmark는 아래와 같습니다. package main import ( "math/rand" "sort" "testing" "time" "golang.org/x/exp/slices" ) func BenchmarkSlicesSortFunc(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() data := generateTestData(1000) // Generate a slice of 1000 integers b.StartTimer() slices.SortFunc(data, func(a, b int) int { return a - b }) } } // Benchmark using sort.Slice func BenchmarkSortSlice(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() data := generateTestData(1000) // Generate a slice of 1000 integers b.StartTimer() sort.Slice(data, func(i, j int) bool { return data[i] < data[j] }) } } // Function to generate test data func generateTestData(n int) []int { rand.Seed(time.Now().UnixNano()) data := make([]int, n) for i := range data { data[i] = rand.Intn(n) // Random integers between 0 and n-1 } return data } goos: darwin goarch: arm64 pkg: exam BenchmarkSlicesSortFunc BenchmarkSlicesSortFunc-10 24333 52203 ns/op 0 B/op 0 allocs/op BenchmarkSortSlice BenchmarkSortSlice-10 18000 62061 ns/op 56 B/op 2 allocs/op PASS ok exam 8.010s 정리 지금까지 Golang GC 튜닝 및 튜닝을 위한 여러 테크닉들을 살펴보았습니다. 하지만 튜닝에 있어 가장 중요한 점은 역설적이게도 ‘필요한 만큼만’하고 멈추는 것입니다. 프로그램의 기본은 자료구조와 알고리즘이며 테크닉을 중심으로 세심하게 코드에 공들이는 것보다, 처음부터 좋은 설계로 좋은 구조를 갖도록 하는 것이 훨씬 중요합니다. 과도한 튜닝은 코드의 가독성과 생산성을 떨어뜨리고 코드의 유지보수를 어렵게 만들어, 더 좋은 구조로 바꾸기 어렵게 만드는 장애물이 될 수 있기 때문입니다. 잘 조리된 요리의 풍미를 살리는 마지막 향신료 한 방울처럼, 독자분들이 항상 적절한 정도로 코드를 튜닝하시길 바랍니다. Reference [1] https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html https://www.ardanlabs.com/blog/2019/05/garbage-collection-in-go-part2-gctraces.html https://www.ardanlabs.com/blog/2019/07/garbage-collection-in-go-part3-gcpacing.html [2] https://tip.golang.org/doc/gc-guide#Optimization_guide [3] https://tip.golang.org/doc/gc-guide [4] https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i-learnt-to-stop-worrying-and-love-the-heap/ [5] https://go.dev/doc/faq#stack_or_heap [6] https://medium.com/invalid-memory/golang-cost-of-using-the-heap-e70363469754 [7] https://pkg.go.dev/fmt#Print [8] https://github.com/golang/go/issues/15528 [9] https://articles.wesionary.team/use-case-of-pointers-in-go-w-practical-example-heap-stack-pointer-receiver-60b8950473da [10] https://github.com/golang/go/issues/37975 https://go.dev/src/cmd/compile/internal/ir/cfg.go written by Yanoo.kim edited by June.6
Chrome Devtools를 활용하여 나만의 웹뷰 디버깅 환경 만들기 / 제5회 Kakao Tech Meet
kakao tech · 5달 전
4월 11일에 진행한 제5회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #웹뷰 #디버깅 #ChromeDevTools #websocket 발표자 Teenie.jo(조민지님) 인터뷰 테크밋에 참여하게 된 계기는 무엇이었나요? 아무래도 담당 업무가 아니라 사이드 프로젝트이다 보니 후순위로 밀려 프로젝트에 적극적으로 기여하지 못하는 느낌이 들었어요. 그래서 이렇게라도 일정을 잡아서 적극적으로 진행하고자 하는 마음 절반, 개인적으로 발표를 너무 무서워하여서 이렇게 조금씩 해보자 하는 마음 절반으로 시작했습니다. 처음엔 사내 테크톡인 줄 알고 참여하였는데 알고 보니 외부 분들이 참여하시는 테크밋인 것을 알게 되고 멘붕이 왔었어요. 팀 안에서 하는 발표에도 목소리가 양처럼 떨리는데… 외부 분들이 오는 발표라니🤯 발표를 준비하면서 가장 중요하게 생각한 부분은 무엇인가요? 제가 말을 또박또박 잘 들리게 하는 편은 아닌 것 같아 발표 자료에 최대한 제가 하는 말을 시각화하여 담기 위해 노력했습니다. 가령 프로토콜이 통신하는 것 자체가 생소하다 보니 말로만 설명해서는 이해가 어려울 것 같아 프로토콜 메시지 같은 것들이 실제로 이동하듯이 보여주고자 하였고, 간단한 예시 코드나 이미지도 최대한 함께 보여주려고 노력하였습니다. 또한 제가 개발을 하면서 느꼈던 평소의 디버깅 환경부터 어떠한 것을 필요로 하였고, 이를 위해 무엇을 조사하고 마지막으로 어떻게 개발하였는지와 같은 생각의 흐름을 담고 싶어서 이야기를 빌드업 해나가는 과정을 중요하게 생각하며 슬라이드 순서를 많이 수정해가면서 발표를 준비하였던 것 같습니다. 패널토의에 참여하신 후기가 궁금해요! 발표만큼 걱정되었던 세션이었는데요, 예상하지 못한 질문도 있었고 속으로 예상했던 질문들도 있어 의외로 흥미롭고 즐겁게 참여하였습니다. 하지만 생각보다 질문들이 빨리 지나가고 자리에서 질문이 잘 안 보여 머릿속에서 미리 질문에 대한 답변을 생각해 볼 시간이 부족했어요. 그래서 더 좋은 답변을 드리지 못한 것이 아쉬움이 남습니다. 그럼에도 불구하고 청취자분들께서 끄덕끄덕 해주시며 계속해서 리액션을 주셔서 용기를 가지고 답변을 이어나갈 수 있었습니다! 못다 한 이야기가 있다면? 저희 디버거 정말 유용한데, 자랑을 많이 하지 못하고 기술적인 설명 위주로 발표한 것 같아 조금 아쉽습니다ㅎㅎ 발표를 준비하면서 도움을 준 크루들에게 전하고 싶은 말이 있나요? 테크밋 날짜가 다가올수록 저의 머릿속이 온통 발표 준비로 가득 차서, 메인 프로젝트 업무와 웹뷰 디버거 개발에 많이 참여하지 못하여서 동료분들에게 죄송했다는 말씀을 전하고 싶습니다. 그럼에도 불구하고 셀원들 모두가 언제나 응원해 주시고 적극적으로 피드백 주신 덕분에 잘 마칠 수 있었던 것 같습니다! 정말 감사합니다🙇🏻♀️ 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: [Phocus] 사용자 경험 중심 스벨트 이미지 뷰어 라이브러리 / 제5회 Kakao Tech Meet 웹 텍스트 에디터 개발에 필요한 고민과 신규 에디터 소개 / 제5회 Kakao Tech Meet Chrome Devtools를 활용하여 나만의 웹뷰 디버깅 환경 만들기 / 제5회 Kakao Tech Meet
웹 텍스트 에디터 개발에 필요한 고민과 신규 에디터 소개 / 제5회 Kakao Tech Meet
kakao tech · 5달 전
4월 11일에 진행한 제5회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #TextEditor #IME #FrontendArchitecture 발표자 Eliot.p(박민규님) 인터뷰 발표를 준비하면서 가장 많은 시간 고민을 한 부분은 무엇인가요? 다양한 분야의 개발자분들이 오시는 만큼 어떤 내용을 준비해야 할지 많이 고민했습니다. 도메인이 특수하다 보니 파고들수록 할 얘기는 많지만, 그렇게 하면 다수의 개발자분들과 공감대를 찾기는 어려울 것이라 생각했어요. 그래서 다른 배경을 가진 참여자들에게도 도움이 될 수 있도록, 웹 텍스트 에디터 개발에 국한되지 않는 일반적인 소프트웨어 공학적 문제해결 내용처럼 보이도록 신경을 많이 썼던 것 같아요. 얘기하고 싶은 내용은 많았지만 약속한 시간 안에 전달드리려 하다 보니 효과적으로 내용을 구성하기 위해 발표자료를 만드는 데에 많은 시간을 썼습니다. 욕심을 많이 버렸음에도 발표가 생각보다는 길어진 것 같아요. 패널토의에 참여하신 후기가 궁금해요! 이렇게 공개적인 자리에 서는 것은 처음이다 보니 긴장을 많이 했어요. 게다가 실시간으로 질문에 대한 답변을 드려야 하니 머리가 멍해지기도 했습니다. 평소에 고민하던 것에 비해 말과 생각이 자꾸 꼬이는 것 같았고, 그래서 더 명쾌한 대답을 드리지 못한 것 같아 죄송하고 아쉬웠습니다. 이후에 더 편안한 환경에서 관심 있으신 분들과 토의할 수 있으면 좋겠다고 생각했어요. 못다 한 이야기가 있다면? 말씀드린 것과 같이, 웹 텍스트 에디터 발표이긴 하지만 최대한 웹 텍스트 에디터 발표처럼 보이지 않도록 신경 썼기 때문에, 프로젝트 자체에 대한 이야기는 거의 하지 않았습니다. 그러다 보니 내부 구조라든가, 좀 더 설계에 대해서도 궁금하시지 않았을까 싶습니다. 여담으로, 발표가 너무 길어질까 봐 발표를 하는 중간에도 준비했던 스크립트를 즉흥적으로 줄이기도 했어요. 이번 테크밋을 통해 느낀 것을 바탕으로, 올 한 해 좀 더 완성도를 높여서 지금보다 더 구체적인 내용으로 더 발전된 발표를 할 수 있는 기회가 있으면 좋겠습니다. 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: [Phocus] 사용자 경험 중심 스벨트 이미지 뷰어 라이브러리 / 제5회 Kakao Tech Meet 웹 텍스트 에디터 개발에 필요한 고민과 신규 에디터 소개 / 제5회 Kakao Tech Meet Chrome Devtools를 활용하여 나만의 웹뷰 디버깅 환경 만들기 / 제5회 Kakao Tech Meet
[Phocus] 사용자 경험 중심 스벨트 이미지 뷰어 라이브러리 / 제5회 Kakao Tech Meet
kakao tech · 5달 전
4월 11일에 진행한 제5회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. #Svelte #ImageViewer #VirtualSlide #Library 발표자 Jordan.song(송가람님) 인터뷰 어떻게 테크밋에서 발표를 하게 되었나요? 발표에서도 말씀드렸다시피 이미지뷰어 라이브러리 개발은 사내 사이드 프로젝트로 시작했어요. 그렇다 보니 기획도 개발자가 주도했고, 프로젝트도 1년 반이라는 긴 시간 동안 진행하였어요. 오랜 기간 동안 프로젝트를 진행하다 보니 다들 프로젝트에 대한 애정도 있었고, 그에 걸맞은 프로젝트 결과도 나왔다고 생각했어요. 우리가 만든 좋은 결과와 오랜 기간 동안 고생했던 진행 과정을 공유하여 대내외적으로 사람들에게 좋은 영향력을 주고 싶어 발표를 하게 되었습니다. 발표를 준비하면서 중요하게 고민한 것은 무엇이었나요? 20분이라는 짧은 발표 시간에 1년 반의 긴 이야기를 어떻게 담아낼 수 있을까 고민했어요. 어떻게 하면 ‘이미지뷰어 라이브러리가 무엇인지’, ‘어떤 기능을 가지고 있는지’를 청중들에게 잘 전달하고 싶었고, 우리가 고민했던 기술적인 이야기도 놓치고 싶지 않았어요. 그래서 이미지뷰어를 소개하는 내용과 기술적인 리뷰를 하는 내용의 적절한 밸런스를 맞추려고 노력했고, 개발을 모르는 사람이 들어도 내용을 이해할 수 있도록 최대한 노력하며 준비했습니다. 그런 고민을 오랫동안 했기 때문에 괜찮은 발표 결과를 만들어내지 않았나 싶어요. 발표를 마친 소감을 들려주세요! 리허설을 할 때는 발표 시간을 맞추기 어려웠고 준비했던 내용도 많이 이야기하지 못해서 아쉬웠는데요, 그래서 실제 발표에서 잘할 수 있을까 걱정했어요. 하지만 막상 발표를 시작하니 제 발표를 경청해 주시는 분들의 모습이 보였어요. 그래서 발표 중간에 청중들에게 내가 가진 지식을 조금이라도 더 이해하기 좋게 전달드려야겠다는 생각을 하게 되었고, 내용을 더 꼼꼼하게 전달하는데 집중했어요. 결과적으로 리허설 때보다 더 만족스러운 발표를 하게 된 것 같아요. 그리고 패널 토의를 통해 그동안 깊게 고민해보지 않았던 내용들을 고민해 볼 수 있어서 좋았어요. 발표만 하고 마무리가 되었다면 조금 아쉬웠을 것 같은데 패널토의 세션을 잘 준비해 주셔서 발표 마무리를 더 제대로 한 기분이었어요! 앞으로 계획하거나 기대하는 것이 있나요? 다음 달이면 이미지뷰어를 실제 서비스에 적용할 계획을 가지고 있어요. 라이브러리를 직접 배포하고 관리해 본 단계까지는 발표하지 못한 부분들이 아쉬웠는데, 라이브러리를 직접 관리해 보면 또 다른 경험일 것 같아 앞으로가 기대됩니다! 발표 영상은 카카오테크 유튜브 채널(재생목록)에서도 시청하실 수 있습니다. 📚관련 글 목록: [Phocus] 사용자 경험 중심 스벨트 이미지 뷰어 라이브러리 / 제5회 Kakao Tech Meet 웹 텍스트 에디터 개발에 필요한 고민과 신규 에디터 소개 / 제5회 Kakao Tech Meet Chrome Devtools를 활용하여 나만의 웹뷰 디버깅 환경 만들기 / 제5회 Kakao Tech Meet
제5회 Kakao Tech Meet에 초대합니다!
kakao tech · 6달 전
Kakao Tech Meet #5 트렌드와 경험 및 노하우를 자주, 지속적으로 공유하며 개발자 여러분과 함께 성장을 도모하고 긴밀한 네트워크를 형성하고자 합니다. 다섯 번째 Kakao Tech Meet에서는 웹 프론트엔드를 주제로 이미지 뷰어 라이브러리, 웹 에디터, 웹뷰 디버깅 등의 이야기를 준비했습니다. 또한 발표에서 못다 한 질문과 이야기를 나눌 수 있도록 패널토의를 진행합니다. 판교역에 연결된 카카오 아지트에서 열리는 이번 기술 세미나에 많은 관심과 참여 부탁드립니다. 앞으로도 카카오는 개발자 커뮤니티의 일원으로서, 개발자 커뮤니티와 함께 성장하기 위해서 꾸준히 노력하겠습니다. 일시 4월 11일(목) 19:00 - 21:30 (18:30부터 입장 가능합니다) 장소 카카오판교아지트, 4층 스위치온 / 신분당선 판교역 4번 출구 (경기 성남시 분당구 판교역로 166) 오프라인 참석자에게는 행사장 입장을 위한 ‘웰컴패스’를 카카오톡으로 보내 드립니다. festa에 등록하신 휴대폰 번호가 정확한지 확인해 주세요. 온라인 라이브 스트리밍을 제공하며, 접속 정보는 행사 전 안내드릴 예정입니다. 발표 내용 & 발표자 소개 (1) [Phocus] 사용자 경험 중심 스벨트 이미지 뷰어 라이브러리 웹 이미지뷰어 라이브러리 '포커스’의 개발 이야기를 공유합니다. 개발 과정에서 선택한 프레임워크인 '스벨트’에 대한 결정 과정과 개인적인 경험에 대해서 이야기합니다. 그리고 이미지 뷰어를 개발하면서 겪었던 다양한 이슈들과 이를 어떻게 해결해 나갔는지 공유합니다. 웹 페이지 및 라이브러리 개발에 관심 있는 모든 FE 개발자에게 추천합니다. #Svelte #ImageViewer #VirtualSlide #Library 목차 이미지 뷰어 라이브러리 개발 배경 개발 과정에서 선택한 주요 기술 스택 이미지 뷰어의 주요 기능 소개와 성능 최적화 과정 사용자 경험을 향상시키기 위한 숨겨진 디테일 발표자: 송가람(jordan.song) 카카오의 콘텐츠FE에서 티스토리 서비스를 운영 및 개발하고 있는 조단입니다. 전사 이미지 뷰어 라이브러리 포커스의 프로젝트 리더로 기획 및 개발을 진행하였습니다. (2) 웹 텍스트 에디터 개발에 필요한 고민과 신규 에디터 소개 웹 텍스트 에디터 개발의 근본적인 문제를 정의하고 제시합니다. 카카오에서 신규 에디터를 개발하게 된 배경과 사용 목적을 소개합니다. 에디터 개발 시 마주한 다양한 챌린지를 어떻게 해결하였는지 공유합니다. 마지막으로 웹 텍스트 에디터 개발 관련 논의되고 있는 미래의 웹 API 스펙을 간략히 소개합니다. 툴체인을 넘어 근본적인 문제 해결에 관심 있는 프론트엔드 개발자들에게 추천합니다. #TextEditor #IME #FrontendArchitecture 목차 웹 텍스트 에디터 개발 신규 에디터 개발 배경 신규 에디터 개발 기술적 챌린지 해결 논의 중인 에디터 관련 Web API 스펙 발표자: 박민규(eliot.p) | GitHub, 블로그 로컬FE에서 확장형 웹 텍스트 에디터 개발을 하고 있습니다. 기술 이전에 기술 윤리와 같은 철학적 사유에도 관심이 많습니다. (3) Chrome Devtools를 활용하여 나만의 웹뷰 디버깅 환경 만들기 웹뷰 서비스를 개발하는 과정에서 겪었던 불편함과, 이를 해결하기 위해 새로운 웹뷰 디버깅 환경을 구축해 나간 이야기를 소개합니다. ChromeDevTools의 오픈소스인 devtools-frontend와 devtools-protocol을 활용하여 익숙하면서도 더욱 편리하게 웹뷰 디버깅을 수행할 수 있는 환경을 만든 과정을 공유드립니다. 웹뷰와 같이 디버깅을 하기 복잡한 환경에서 서비스를 개발하는 프론트엔드 개발자들에게 추천합니다. #웹뷰 #디버깅 #ChromeDevTools #websocket 목차 웹뷰 환경이란? 웹뷰를 디버깅하기 위한 방법들 새로운 웹뷰 디버깅 환경 Chrome Devtools와 Protocol 웹뷰↔️Devtools 통신 발표자: 조민지(teenie.jo) 톡유저서비스FE의 티니입니다! 잔여백신, 전자증명서, 전자문서 등 카카오톡 지갑의 다양한 웹뷰 서비스를 개발하고 있습니다 :) ★ 안내 공간 제약상, 오프라인 참여를 신청해 주신 분들 중 일부만 초대드리는 점 양해 부탁드립니다. 행사 안내는 카카오톡으로 제공됩니다. (등록하신 휴대폰 번호가 정확하지 않거나 카카오톡과 연결되지 않은 경우, 안내를 드리기 어렵습니다.) 오프라인 참석이 선정된 분들께는 4/4(목) 이후, 카카오톡으로 안내드릴 예정입니다. 온라인 라이브로 모든 분들이 참여하실 수 있습니다. 행사 전 접속 정보를 안내드릴 예정입니다. 발표 세션의 녹화 영상은 행사 종료 후 제공드릴 예정입니다(유튜브 @kakaotech 및 기술 블로그 tech.kakao.com). 발표자료 파일은 제공되지 않습니다. 간단한 다과와 기념품을 드립니다. 식사는 별도로 제공하지 않습니다. 문의 tech@kakaocorp.com 신청하러 가기💌 https://festa.io/events/4919