logo
emoji
검색하기
어제 가장 많이 검색된 기술
emoji
가장 많이 읽은 글
logo
데이터 문해력이 필요해(Data Literacy)
업무에서 데이터 활용은 많은데..지난 3월, 각 부서에서 "어떤 데이터를 어떻게 활용 하고 있는지" 알아 보기 위한 설문을 진행했었어요.업무 전반(end to end)에서 필요한 데이터가 제대로 수집되고 있는지, 수집된 데이터는 쓰이기 쉬운 형태로 가공은 잘 되는지,그리고 가공된 데이터는 의사결정에 어느 정도로 활용되고 있는 지 등을 알아보았습니다.온라인 서베이/대면 인터뷰를 모두 활용하여 최대한 많은 의견을 듣고자 노력했고, 결론을 간단히 요약하면 아래와 같습니다."다수의 구성원(76%)이 매일 자신의 업무 전반에서 데이터를 폭넓게 활용하고 있다고 답변. 깊이 있는 분석의 필요성은 커지고 있으나,분석 환경 및 필요한 역량이 부족하여 인사이트를 빠르게 도출하지 못하고 있음"요악하면 위와 같았습니다.놀랐던 점은 생각 보다 많은 구성원이 업무에 데이터를 매일 활용하고 있다는 것이었고, 데이터에서 인사이트를 발견하기 쉽지 않다는 점에는 공감이 되었습니다.'무엇이 인사이트일까'라는 어려운 질문은 잠시 뒤로 하고,"우리는 왜 데이터에서 인사이트를 발견하기 쉽지 않을까" 고민 해보면 결국 데이터를 해석하는 역량(Data Literacy)에 대해 생각하게 됩니다.사내에는 수많은 대시보드가 있고, 공유 되는 엑셀에는 숫자로 가득 차 있는데, 이 그래프와 숫자 이 의미하는 바를 정확히 이해하기 어려운 것이죠.저 역시 분석 업무를 하면서 '이 숫자가 정말 의미하는 바가 뭘까?'라고 고민하는 경우가 많았습니다.더 당황스러운 건, 처음에는 명확해 보였던 결과가 다른 각도에서 보면 완전히 다른 이야기를 하는 경우였죠.이런 경험을 통해 알게된 것은 데이터 해석에는 생각보다 많은 함정이 숨어있다는 점입니다.그래서, 이번 글에는 데이터 '해석' 단계에서 자주 등장하는 함정들을 실제 사례와 함께 보려고 합니다.부분과 전체가 엇갈리는 순간 - 심슨의 역설(simpson's paradox)심슨의 역설은 많이 알려진 일종의 통계 착시인데, 이에 대한 예시로는 위 야구 통계가 자주 활용합니다.메이저리그에서 있었던 실제 데이터인데, 데릭 지터는 95,96,97 세 번의 시즌 동안 데이빗 저스티스보다 낮은 타율을 기록했지만,이 세 번의 시즌을 모두 합하면 오히려 더 높은 타율을 기록했습니다.매시즌 지터 보다 더 좋은 타자였던 저스티스이지만, 3년 동안 지터 보다 더 못한 타자가 되는 것입니다.이런 결론이 나오는 이유는 타석(at-bats)을 보면 이해가 됩니다.지터는 3년 동안 가장 활약이 저조 했떤 95년의 타석이 매우 적은 반면(48), 저스티스는 가장 나쁜 타율을 기록한 95년의 타석이 상대적으로 매우 많습니다(411).이런 비율/분포의 차이가 평균에 영향을 준 것이지요. 이런 현상을 심슨의 역설이라 하고, 실무에서도 볼 수 있습니다.MMS 캠페인에서의 A/B 테스트는 주로 프로모션에 대한 효과성을 검증하거나, 같은 프로모션에서의 이미지/문구의 차이를 확인하기 위해 진행하는 경우가 많습니다.현재 사내의 MMS 캠페인 시스템은 별도의 조작 없이도(모수 할당 등) A/B 테스트를(RCT:랜덤할당)를 지원하는 상황입니다.위 자료는 상품에 대한 문구 차이를 검증하기 위한 테스트 마케팅이었고(타입A; 상품강조, 타입B: 고객관심강조) 타입 A의 반응율(클릭여부)이 더 높은 것으로 보입니다.하지만, 데이터는 좀 더 쪼개볼수록 얻을 수 있는 인사이트도 늘어난다는 신념(?)하에 성별까지 쪼개보면 얘기가 달라집니다.남성/여성에서 모두 타입 B의 반응율이 높았죠.이 역시 위 야구 통계에서와 같이 성별 모수의 불균형이 영향을 끼친 것인데, '와 신기하다' 정도로 마무리하면 좋겠지만 우리는 결론을 내려야 합니다.성별을 맞추어 다시 테스트 하기에는 시간이 없다고 한다면, 지금 우리는 앞으로의 MMS 발송에서 어떤 타입을 발송해야 될까요.우리 문제의 결론에 다가가기에 앞서, 좀 더 일반화해서 이 문제를 바라보도록 하죠.인과 추론의 선구자 주데아 펄(Judea Pearl)은 이런 문제, 즉 데이터에서 인과관계를 도출하는 어려움에 대해 다음과 같이 말했습니다.“관찰된 통계값만으로는 인과관계를 말할 수 없다. 무엇을 통제하고 제거해야 하는지를 알기 위해서는 변수들 간의 구조를 명시적으로 가정해야 한다."또한 펄은 '심슨의 역설'이 일어나는 상황에 대해서도" 표/그래프에서 보이는 수치의 역전현상(simpsons's reversal)인지,아니면 실제 인과적 모순(paradox)인지를 구분해야 한다"고 강조합니다.그의 책 the book of why에서는 인과 구조를 고려하지 않은 상황에서의 통계 해석은 자칫 오해를 불러올 수 있으며,이런 상황을 해결해줄 도구로 자신이 만든 DAG(Directed Acyclic Graph)를 제시합니다.DAG는 인과적 추론을 가능하게 하는 그래프 모델로, 변수간의 인과관계를 수치적 계산에 기대기 보다는(model-blind),맥락과 도메인 지식(extra-statstical knowledge)으로 바라봅니다.우리 데이터의 변수 관계를 DAG로 표현하면 위와 같이 두가지 형태를 생각해볼 수 있는데,왼쪽은 성별이 MMS타입 할당과 캠페인 반응 모두에 영향을 준 모습이고, 오른쪽은 성별이 MMS 할당에는 영향을 주지 않은 모습입니다.캠페인 테스트에서 할당은 시스템에서 랜덤으로 할당을 했기에 오른쪽의 형태여야 하지만(성별이 캠페인 할당에 영향을 주지 않은),이번 표본에서는 성별 분포가 불균형 했으므로, 우리는 교란 요인이 있다는 것을 염두에 두고, 이를 정량적으로 교정하여 실제 효과를 측정해야 합니다.관찰 데이터에서 인과관계를 추정하는 작업은 여러 방법론이 있지만 여기서는 가장 간단하게,각 응답률의 차이에 실제 고객 성비를 가중치를 주는 방식으로 진행하여, 아래와 같이 계산해볼 수 있습니다.위 숫자를 보고 우리가 내릴수 있는 결론은 B를 택하는 것이 맞다는 것입니다.A/B Test를 통해 전체의 효과는 A의 응답률이 높았지만,이는 실험상의 성별 불균형으로 인한 착시에 가깝고 다음에 우리가 보내야 될 MMS 타입은(이번 테스트를 성별로 나눈 결과 대로) B를 택하는 것이 맞다는 것이지요.남성에서도 반응이 좋고, 여
6/5/2025
logo
데이터 문해력이 필요해(Data Literacy)
업무에서 데이터 활용은 많은데..지난 3월, 각 부서에서 "어떤 데이터를 어떻게 활용 하고 있는지" 알아 보기 위한 설문을 진행했었어요.업무 전반(end to end)에서 필요한 데이터가 제대로 수집되고 있는지, 수집된 데이터는 쓰이기 쉬운 형태로 가공은 잘 되는지,그리고 가공된 데이터는 의사결정에 어느 정도로 활용되고 있는 지 등을 알아보았습니다.온라인 서베이/대면 인터뷰를 모두 활용하여 최대한 많은 의견을 듣고자 노력했고, 결론을 간단히 요약하면 아래와 같습니다."다수의 구성원(76%)이 매일 자신의 업무 전반에서 데이터를 폭넓게 활용하고 있다고 답변. 깊이 있는 분석의 필요성은 커지고 있으나,분석 환경 및 필요한 역량이 부족하여 인사이트를 빠르게 도출하지 못하고 있음"요악하면 위와 같았습니다.놀랐던 점은 생각 보다 많은 구성원이 업무에 데이터를 매일 활용하고 있다는 것이었고, 데이터에서 인사이트를 발견하기 쉽지 않다는 점에는 공감이 되었습니다.'무엇이 인사이트일까'라는 어려운 질문은 잠시 뒤로 하고,"우리는 왜 데이터에서 인사이트를 발견하기 쉽지 않을까" 고민 해보면 결국 데이터를 해석하는 역량(Data Literacy)에 대해 생각하게 됩니다.사내에는 수많은 대시보드가 있고, 공유 되는 엑셀에는 숫자로 가득 차 있는데, 이 그래프와 숫자 이 의미하는 바를 정확히 이해하기 어려운 것이죠.저 역시 분석 업무를 하면서 '이 숫자가 정말 의미하는 바가 뭘까?'라고 고민하는 경우가 많았습니다.더 당황스러운 건, 처음에는 명확해 보였던 결과가 다른 각도에서 보면 완전히 다른 이야기를 하는 경우였죠.이런 경험을 통해 알게된 것은 데이터 해석에는 생각보다 많은 함정이 숨어있다는 점입니다.그래서, 이번 글에는 데이터 '해석' 단계에서 자주 등장하는 함정들을 실제 사례와 함께 보려고 합니다.부분과 전체가 엇갈리는 순간 - 심슨의 역설(simpson's paradox)심슨의 역설은 많이 알려진 일종의 통계 착시인데, 이에 대한 예시로는 위 야구 통계가 자주 활용합니다.메이저리그에서 있었던 실제 데이터인데, 데릭 지터는 95,96,97 세 번의 시즌 동안 데이빗 저스티스보다 낮은 타율을 기록했지만,이 세 번의 시즌을 모두 합하면 오히려 더 높은 타율을 기록했습니다.매시즌 지터 보다 더 좋은 타자였던 저스티스이지만, 3년 동안 지터 보다 더 못한 타자가 되는 것입니다.이런 결론이 나오는 이유는 타석(at-bats)을 보면 이해가 됩니다.지터는 3년 동안 가장 활약이 저조 했떤 95년의 타석이 매우 적은 반면(48), 저스티스는 가장 나쁜 타율을 기록한 95년의 타석이 상대적으로 매우 많습니다(411).이런 비율/분포의 차이가 평균에 영향을 준 것이지요. 이런 현상을 심슨의 역설이라 하고, 실무에서도 볼 수 있습니다.MMS 캠페인에서의 A/B 테스트는 주로 프로모션에 대한 효과성을 검증하거나, 같은 프로모션에서의 이미지/문구의 차이를 확인하기 위해 진행하는 경우가 많습니다.현재 사내의 MMS 캠페인 시스템은 별도의 조작 없이도(모수 할당 등) A/B 테스트를(RCT:랜덤할당)를 지원하는 상황입니다.위 자료는 상품에 대한 문구 차이를 검증하기 위한 테스트 마케팅이었고(타입A; 상품강조, 타입B: 고객관심강조) 타입 A의 반응율(클릭여부)이 더 높은 것으로 보입니다.하지만, 데이터는 좀 더 쪼개볼수록 얻을 수 있는 인사이트도 늘어난다는 신념(?)하에 성별까지 쪼개보면 얘기가 달라집니다.남성/여성에서 모두 타입 B의 반응율이 높았죠.이 역시 위 야구 통계에서와 같이 성별 모수의 불균형이 영향을 끼친 것인데, '와 신기하다' 정도로 마무리하면 좋겠지만 우리는 결론을 내려야 합니다.성별을 맞추어 다시 테스트 하기에는 시간이 없다고 한다면, 지금 우리는 앞으로의 MMS 발송에서 어떤 타입을 발송해야 될까요.우리 문제의 결론에 다가가기에 앞서, 좀 더 일반화해서 이 문제를 바라보도록 하죠.인과 추론의 선구자 주데아 펄(Judea Pearl)은 이런 문제, 즉 데이터에서 인과관계를 도출하는 어려움에 대해 다음과 같이 말했습니다.“관찰된 통계값만으로는 인과관계를 말할 수 없다. 무엇을 통제하고 제거해야 하는지를 알기 위해서는 변수들 간의 구조를 명시적으로 가정해야 한다."또한 펄은 '심슨의 역설'이 일어나는 상황에 대해서도" 표/그래프에서 보이는 수치의 역전현상(simpsons's reversal)인지,아니면 실제 인과적 모순(paradox)인지를 구분해야 한다"고 강조합니다.그의 책 the book of why에서는 인과 구조를 고려하지 않은 상황에서의 통계 해석은 자칫 오해를 불러올 수 있으며,이런 상황을 해결해줄 도구로 자신이 만든 DAG(Directed Acyclic Graph)를 제시합니다.DAG는 인과적 추론을 가능하게 하는 그래프 모델로, 변수간의 인과관계를 수치적 계산에 기대기 보다는(model-blind),맥락과 도메인 지식(extra-statstical knowledge)으로 바라봅니다.우리 데이터의 변수 관계를 DAG로 표현하면 위와 같이 두가지 형태를 생각해볼 수 있는데,왼쪽은 성별이 MMS타입 할당과 캠페인 반응 모두에 영향을 준 모습이고, 오른쪽은 성별이 MMS 할당에는 영향을 주지 않은 모습입니다.캠페인 테스트에서 할당은 시스템에서 랜덤으로 할당을 했기에 오른쪽의 형태여야 하지만(성별이 캠페인 할당에 영향을 주지 않은),이번 표본에서는 성별 분포가 불균형 했으므로, 우리는 교란 요인이 있다는 것을 염두에 두고, 이를 정량적으로 교정하여 실제 효과를 측정해야 합니다.관찰 데이터에서 인과관계를 추정하는 작업은 여러 방법론이 있지만 여기서는 가장 간단하게,각 응답률의 차이에 실제 고객 성비를 가중치를 주는 방식으로 진행하여, 아래와 같이 계산해볼 수 있습니다.위 숫자를 보고 우리가 내릴수 있는 결론은 B를 택하는 것이 맞다는 것입니다.A/B Test를 통해 전체의 효과는 A의 응답률이 높았지만,이는 실험상의 성별 불균형으로 인한 착시에 가깝고 다음에 우리가 보내야 될 MMS 타입은(이번 테스트를 성별로 나눈 결과 대로) B를 택하는 것이 맞다는 것이지요.남성에서도 반응이 좋고, 여
2025.06.05
emoji
좋아요
emoji
별로에요
logo
[동료 생각 좇기] (3) 세상에 없던, 시술 정보 표준화 프로젝트의 긴 여정
안녕하세요. 강남언니 커뮤니케이션 리더 Joanne 입니다.강남언니 팀에서 채워지는 기록과 대화들이 많아질수록, 소개하고 싶은 우리의 이야깃거리가 참 많아졌어요. 그 이야기를 [동료 생각 좇기] 코너로 담고 있습니다.오늘 다루는 이 문제가 해결되면 고객이 피부 시술 정보를 찾는 어려움이 한결 개선될 수 있어요. 강남언니 앱에서 봤던 시술 가격과 실제 피부과에서 안내한 가격이 다른 상황이 발생하는 이유는, 단순히 병원이 잘못된 정보를 제공한 것을 넘어 다양한 맥락과 이해관계가 숨어있는데요.이 문제를 해결하기 위해 고객의 시술 여정, 병원의 정보 제공 과정 전반을 고민해야 했던, 업계에 선례가 없음에도 시술 정보 표준화 프로젝트 이름을 내걸고 장기적인 문제를 해결하고 있는 PO Steve, Product Designer Selah와의 대화를 좇아가봅니다.강남언니의 시술 정보 표준화 프로젝트가 반영된 검색 결과 페이지(1) 문제 정의: 제대로 된 시술 가격 정보를 원하는 고객을 위해👧🏻 Joanne | 강남언니 팀에서 미용의료 고객의 “시술 여정” 을 고민하고 있는 두 분, 각자 소개 부탁 드려요.👨🏼‍🦰 Steve | 강남언니 인천 스쿼드의 PO를 맡고 있어요. (참고로 스쿼드 이름은 도메인과 무관해요) 고객이 가진 고민을 해결할 최적의 미용의료 병원과 의사를 찾는 데 더 좋은 경험을 누리도록 고민하는 스쿼드입니다.👱🏻‍♀️ Selah | 인천 스쿼드의 프로덕트 디자이너예요. 고객이 원하는 정보를 강남언니에서 잘 발견하고 가장 적합한 시술을 선택하기까지의 시술 여정을 설계하는 일을 하고 있어요.👧🏻 Joanne | 인천 스쿼드는 단순히 고객에게 많은 시술 정보를 보여주는 것이 아닌 체계적이고 맞춤화된 시술 정보를 제공하는 데 집중하고 있어요. 이러한 목표를 설정한 계기가 궁금해요.👨🏼‍🦰 Steve | 고객이 미용의료를 탐색하는 여정을 그려봤을 때, 기술적으로 검색 결과가 잘 나오게 하는 차원을 넘어 더 깊은 요인을 함께 연구해야 했어요. 예를 들어 '인모드 리프팅'을 검색한 고객마다 리프팅 기기와 마취 방법 등 개인이 원하는 시술 옵션이 다를 수 있거든요. Selah가 병원 정보를 체계화하는 방법을 오래 전부터 고민해주고 계셔서 많은 도움을 받고 있어요.👧🏻 Joanne | Selah가 시술 정보를 체계화하는 일에 관심을 갖게 된 계기가 있었나요?👱🏻‍♀️ Selah | 고객은 시술의 정확한 가격 정보를 가장 궁금해하기 때문에, 이들이 가격 정보를 찾는 탐색 비용을 줄여주고 싶었어요. 고객이 한 번에 자신이 원하는 정보를 찾지 못하면 수많은 후기, 의사, 병원정보 세부 페이지를 일일이 방문하는 등 꽤나 복잡도 높은 경험을 하고 있었거든요. 이를 개선하기 위해 가격 정보 구조화를 목표로 하는 HPP(Healingpaper Product Proposal) 문서를 팀에 제안했고, 이것이 현재의 "시술 정보 표준화 프로젝트"로 발전했습니다.👧🏻 Joanne | 성형이 아닌 피부시술에 먼저 집중하셨던데, 특별한 이유
6/5/2025
logo
[동료 생각 좇기] (3) 세상에 없던, 시술 정보 표준화 프로젝트의 긴 여정
안녕하세요. 강남언니 커뮤니케이션 리더 Joanne 입니다.강남언니 팀에서 채워지는 기록과 대화들이 많아질수록, 소개하고 싶은 우리의 이야깃거리가 참 많아졌어요. 그 이야기를 [동료 생각 좇기] 코너로 담고 있습니다.오늘 다루는 이 문제가 해결되면 고객이 피부 시술 정보를 찾는 어려움이 한결 개선될 수 있어요. 강남언니 앱에서 봤던 시술 가격과 실제 피부과에서 안내한 가격이 다른 상황이 발생하는 이유는, 단순히 병원이 잘못된 정보를 제공한 것을 넘어 다양한 맥락과 이해관계가 숨어있는데요.이 문제를 해결하기 위해 고객의 시술 여정, 병원의 정보 제공 과정 전반을 고민해야 했던, 업계에 선례가 없음에도 시술 정보 표준화 프로젝트 이름을 내걸고 장기적인 문제를 해결하고 있는 PO Steve, Product Designer Selah와의 대화를 좇아가봅니다.강남언니의 시술 정보 표준화 프로젝트가 반영된 검색 결과 페이지(1) 문제 정의: 제대로 된 시술 가격 정보를 원하는 고객을 위해👧🏻 Joanne | 강남언니 팀에서 미용의료 고객의 “시술 여정” 을 고민하고 있는 두 분, 각자 소개 부탁 드려요.👨🏼‍🦰 Steve | 강남언니 인천 스쿼드의 PO를 맡고 있어요. (참고로 스쿼드 이름은 도메인과 무관해요) 고객이 가진 고민을 해결할 최적의 미용의료 병원과 의사를 찾는 데 더 좋은 경험을 누리도록 고민하는 스쿼드입니다.👱🏻‍♀️ Selah | 인천 스쿼드의 프로덕트 디자이너예요. 고객이 원하는 정보를 강남언니에서 잘 발견하고 가장 적합한 시술을 선택하기까지의 시술 여정을 설계하는 일을 하고 있어요.👧🏻 Joanne | 인천 스쿼드는 단순히 고객에게 많은 시술 정보를 보여주는 것이 아닌 체계적이고 맞춤화된 시술 정보를 제공하는 데 집중하고 있어요. 이러한 목표를 설정한 계기가 궁금해요.👨🏼‍🦰 Steve | 고객이 미용의료를 탐색하는 여정을 그려봤을 때, 기술적으로 검색 결과가 잘 나오게 하는 차원을 넘어 더 깊은 요인을 함께 연구해야 했어요. 예를 들어 '인모드 리프팅'을 검색한 고객마다 리프팅 기기와 마취 방법 등 개인이 원하는 시술 옵션이 다를 수 있거든요. Selah가 병원 정보를 체계화하는 방법을 오래 전부터 고민해주고 계셔서 많은 도움을 받고 있어요.👧🏻 Joanne | Selah가 시술 정보를 체계화하는 일에 관심을 갖게 된 계기가 있었나요?👱🏻‍♀️ Selah | 고객은 시술의 정확한 가격 정보를 가장 궁금해하기 때문에, 이들이 가격 정보를 찾는 탐색 비용을 줄여주고 싶었어요. 고객이 한 번에 자신이 원하는 정보를 찾지 못하면 수많은 후기, 의사, 병원정보 세부 페이지를 일일이 방문하는 등 꽤나 복잡도 높은 경험을 하고 있었거든요. 이를 개선하기 위해 가격 정보 구조화를 목표로 하는 HPP(Healingpaper Product Proposal) 문서를 팀에 제안했고, 이것이 현재의 "시술 정보 표준화 프로젝트"로 발전했습니다.👧🏻 Joanne | 성형이 아닌 피부시술에 먼저 집중하셨던데, 특별한 이유
2025.06.05
emoji
좋아요
emoji
별로에요
logo
[현장으로 간 리서처] EP1. 커피값은 저희가 낼게요, 테스트 참여 해보실래요?
토스플레이스의 결제 단말기, 프론트를 소개합니다요즘 오프라인 매장에서 자주 보이는 "이쁜" 결제 단말기, 혹시 보신 적 있나요?토스플레이스에서 만든 단말기, 프론트예요. 프론트는 사장님들이 매장을 더욱 효율적으로 운영할 수 있도록 다양한 기능을 제공해요. 키오스크가 될수도 있고, 신메뉴 홍보도 할 수 있고, 토스 이벤트에 참여할 수도 있죠. 얼굴로 결제까지 할 수 있고요!할 수 있는 일이 정말 많지 않나요? 기능이 다양한 만큼, 리서치로 검증해야하는 부분도 많았는데요. 오늘은 그중 하나의 리서치 사례를 소개해드릴게요.카드사 할인 정보를 결제 중에 본다면 어떨까?매장에서 이런 광고를 보신 적 있으신가요? 특정 카드로 결제하면 할인을 받을 수 있다는 내용인데요.이런 정보를 결제하는 도중, 단말기 화면에서 자연스럽게 볼 수 있다면 어떨까요?결제 과정에서 할인 정보를 안내 받을 수 있다면, 더 매끄러운 결제 경험이 되지 않을까요?이 가설을 검증하기 위해 리서치를 시작했어요.실제 결제 단말기에서 카드 할인 정보를 이해할 수 있는지, 내 카드와 연결해 인지할 수 있는지를 확인하는 게 핵심이었죠.하지만 막상 리서치를 어떻게 설계할지 막막했어요. 오프라인 결제 상황을 어떻게 똑같이 구현해 테스트할 수 있을지 고민이 많았거든요. UT룸을 카페처럼 꾸며 상황을 최대한 구체적으로 연출해보는 것도 생각해봤지만 결국 신뢰도에 한계가 있을 것 같았어요.혼자 조용히 커피 마시며 결제할 때, 친구들과 수다 떨며 계산할 때, 직장 동료들과 점심시간에 급하게 결제할 때…고객의 주의력과 행동은 상황에 따라 크게 달라지는데요. UT룸 같이 인위적인 환경에서는 이런 맥락을 제대로 반영하기 어려우니까요.그래서 결심했죠. 진짜 오프라인 매장에서 테스트하자!진짜 매장으로 가다‘심플리시티’라는 카페에 찾아갔어요. 이곳은 토스플레이스가 직접 운영하는 공간으로, 만드는 다양한 오프라인 제품을 실험하기 위해 만들어진 매장이에요. 실제로 손님이 오가는 ‘진짜 매장’이기도 하죠. (참고로 커피 맛집으로도 소문났어요!)실제 고객이 자연스럽게 결제하는 상황에서 단말기 UX를 테스트하면, 인위적인 환경에서는 얻을 수 없는 생생한 반응을 관찰할 수 있다고 생각했어요.현장에서 손님을 리쿠르팅해 게릴라 테스트를 진행하기로 하고, 메뉴를 고르던 고객에게 조심스럽게 다가가 여쭤봤어요.저희가 준비한 시안은 3개였어요.• None 온라인 이커머스와 유사한 퍼널 구조각 시안을 손님들에게 보여드리며, 할인 정보를 얼마나 잘 인지하는지, 자신의 카드와 연결해 실제 결제 행동까지 이어지는지를 중점적으로 관찰했어요.혼자 온 손님부터 친구들과 함께 온 그룹, 점심시간에 급히 결제하는 직장인까지… 다양한 상황에서 고객의 시선 흐름, 주의력, 정보 인지 난이도를 세심하게 살폈죠.테스트는 이틀에 걸쳐 진행했어요. 첫째 날에는 각 시안에 대한 고객 반응을 관찰하며 가설을 정리했고,둘째 날에는 이를 바탕으로 디자인을 수정해 다시 테스트했어요.가설이 검증되는 순간을 보다시안 1은 할인 정보가 반복적으로 노출되며 정보 인지도가 높
6/4/2025
logo
[현장으로 간 리서처] EP1. 커피값은 저희가 낼게요, 테스트 참여 해보실래요?
토스플레이스의 결제 단말기, 프론트를 소개합니다요즘 오프라인 매장에서 자주 보이는 "이쁜" 결제 단말기, 혹시 보신 적 있나요?토스플레이스에서 만든 단말기, 프론트예요. 프론트는 사장님들이 매장을 더욱 효율적으로 운영할 수 있도록 다양한 기능을 제공해요. 키오스크가 될수도 있고, 신메뉴 홍보도 할 수 있고, 토스 이벤트에 참여할 수도 있죠. 얼굴로 결제까지 할 수 있고요!할 수 있는 일이 정말 많지 않나요? 기능이 다양한 만큼, 리서치로 검증해야하는 부분도 많았는데요. 오늘은 그중 하나의 리서치 사례를 소개해드릴게요.카드사 할인 정보를 결제 중에 본다면 어떨까?매장에서 이런 광고를 보신 적 있으신가요? 특정 카드로 결제하면 할인을 받을 수 있다는 내용인데요.이런 정보를 결제하는 도중, 단말기 화면에서 자연스럽게 볼 수 있다면 어떨까요?결제 과정에서 할인 정보를 안내 받을 수 있다면, 더 매끄러운 결제 경험이 되지 않을까요?이 가설을 검증하기 위해 리서치를 시작했어요.실제 결제 단말기에서 카드 할인 정보를 이해할 수 있는지, 내 카드와 연결해 인지할 수 있는지를 확인하는 게 핵심이었죠.하지만 막상 리서치를 어떻게 설계할지 막막했어요. 오프라인 결제 상황을 어떻게 똑같이 구현해 테스트할 수 있을지 고민이 많았거든요. UT룸을 카페처럼 꾸며 상황을 최대한 구체적으로 연출해보는 것도 생각해봤지만 결국 신뢰도에 한계가 있을 것 같았어요.혼자 조용히 커피 마시며 결제할 때, 친구들과 수다 떨며 계산할 때, 직장 동료들과 점심시간에 급하게 결제할 때…고객의 주의력과 행동은 상황에 따라 크게 달라지는데요. UT룸 같이 인위적인 환경에서는 이런 맥락을 제대로 반영하기 어려우니까요.그래서 결심했죠. 진짜 오프라인 매장에서 테스트하자!진짜 매장으로 가다‘심플리시티’라는 카페에 찾아갔어요. 이곳은 토스플레이스가 직접 운영하는 공간으로, 만드는 다양한 오프라인 제품을 실험하기 위해 만들어진 매장이에요. 실제로 손님이 오가는 ‘진짜 매장’이기도 하죠. (참고로 커피 맛집으로도 소문났어요!)실제 고객이 자연스럽게 결제하는 상황에서 단말기 UX를 테스트하면, 인위적인 환경에서는 얻을 수 없는 생생한 반응을 관찰할 수 있다고 생각했어요.현장에서 손님을 리쿠르팅해 게릴라 테스트를 진행하기로 하고, 메뉴를 고르던 고객에게 조심스럽게 다가가 여쭤봤어요.저희가 준비한 시안은 3개였어요.• None 온라인 이커머스와 유사한 퍼널 구조각 시안을 손님들에게 보여드리며, 할인 정보를 얼마나 잘 인지하는지, 자신의 카드와 연결해 실제 결제 행동까지 이어지는지를 중점적으로 관찰했어요.혼자 온 손님부터 친구들과 함께 온 그룹, 점심시간에 급히 결제하는 직장인까지… 다양한 상황에서 고객의 시선 흐름, 주의력, 정보 인지 난이도를 세심하게 살폈죠.테스트는 이틀에 걸쳐 진행했어요. 첫째 날에는 각 시안에 대한 고객 반응을 관찰하며 가설을 정리했고,둘째 날에는 이를 바탕으로 디자인을 수정해 다시 테스트했어요.가설이 검증되는 순간을 보다시안 1은 할인 정보가 반복적으로 노출되며 정보 인지도가 높
2025.06.04
emoji
좋아요
emoji
별로에요
logo
USB-C처럼 연결되는 AI 인터페이스, MCP Server를 만들어보자
MCP(Model Context Protocol)는 현재 기준으로 사실상 AI 모델 연동의 표준처럼 사용되고 있는 것 같습니다.2024년 11월, Anthropic이 처음 공개한 이후 빠르게 주목을 받으며 성장세를 이어가고 있는데요.아직 발전 중인 단계이지만, JSON-RPC 기반의 심플한 구조와 다양한 SDK, 프레임워크 제공 덕분에 개발자들에게 큰 관심을 받고 있습니다.MCP는 공식 소개 문서에서도 언급되듯이, ‘AI의 USB-C’로 비유되곤 합니다.하나의 단일 인터페이스로 다양한 기기와 연결할 수 있는 USB-C처럼, MCP 역시 LLM이 다양한 외부 시스템(도구, 데이터 소스 등)과 일관된 방식으로 통신할 수 있게 해줍니다.조금 다른 이야기이지만, 저는 이 개념이 기존의 운영체제(OS) 구조와도 맞닿아 있다고 생각합니다.우리가 사용하는 OS, 특히 리눅스는 디바이스 드라이버를 파일처럼 추상화하여 상호작용하죠.그리고 이 구조는 HAL(Hardware Abstraction Layer)이라는 공통 규약을 바탕으로 구성되어 있습니다.이 구조에서 착안하면, LLM과 MCP를 활용해 다양한 외부 시스템(디바이스, API, 내부 도구 등)을 규격화해 제어할 수 있다는 점이 중요합니다.예를 들어, IoT 디바이스의 경우 그동안 제조사마다 각기 다른 제어 인터페이스를 연동해야 했지만,MCP 기반으로 통일된 인터페이스를 제공하면 LLM을 통해 자연어로 손쉽게 제어가 가능합니다.제조사는 자신의 디바이스에 맞는 MCP Server만 제공하면 되는 구조인 것이죠.실제로 엔트로픽의 Claude는 ‘Computer Use’ 기능을 통해 브라우저를 조작하거나 파일을 열고 편집하는 작업까지 수행할 수 있고,OpenAI 역시 ‘Operator’ 개념을 통해 LLM이 외부 기능과 직접 연동되도록 하는 실험을 진행하고 있습니다.이런 흐름을 보면, 조만간 운영체제 자체를 LLM이 직접 제어하는, 완전히 새로운 형태의 OS가 등장할 수 있으리라 기대됩니다.앞으로 어떤 새로운 프로토콜이 등장할지는 알 수 없지만, 공통 인터페이스라는 구조적 장점만으로도 다양한 가능성을 열어줄 수 있다는 점은 분명해 보입니다.그런 점에서 MCP는 LLM 활용에 있어 하나의 강력한 시작점을 제시하며, 많은 개발자와 기업의 관심을 받고 있는 것 같습니다.이번 포스트에서는 Python 기반으로 MCP(Model Context Protocol)를 적용하는 기본적인 코드 구현 방법을 단계적으로 소개해보려 합니다.MCP Server 구현부터, 간단한 커맨드라인 스크립트를 통한 MCP Tool 호출,마지막으로 Streamlit을 활용해 자연어로 MCP Tool을 실행하는 Agent 생성까지 차근차근 따라가며 실습해보겠습니다.MCP의 핵심은 호스트 애플리케이션이 여러 서버에 연결할 수 있는 클라이언트-서버 아키텍처를 따르는 것입니다.• None MCP 호스트: MCP를 통해 데이터에 액세스하려는 클로드 데스크톱, IDE 또는 AI 도구와 같은 프로그램• None MCP 클라이언트: 서버와 1:1 연결을 유지하는 프로토콜 클라이언트• None 로컬 데이터 소스)을 통해 각각 특정 기능을 노출하는 경량 프로그램: MCP 서버가 안전하게 액세스할 수 있는 내 컴퓨터의 파일, 데이터베이스 및 서비스• None 원격 서비스: MCP 서버가 연결할 수 있는 인터넷을 통해 사용 가능한 외부 시스템(예: API를 통해)위 설명에서 클라이언트-서버 연동 구조라고 하여 MCP Server가 항상 어떤 원격지에 있는 것은 아닙니다.MCP Host, Client, Server가 모두 한 PC에서도 실행될 수 있고 Server만 원격지에 존재하는 경우도 있습니다.MCP Server를 로컬 PC에 설치해서 사용 가능한 도구들이 다양하게 존재합니다. (ex, Desktop Commander)먼저, MCP의 기반이 되는 MCP Server를 직접 만들어보겠습니다. Python 구현체인 FastMCP를 활용해 간단한 MCP Server를 구성해볼 예정인데요.이 포스트에서는 복잡한 내부 구조보다는 실행에 초점을 맞춰, 빠르게 테스트해볼 수 있는 최소한의 형태로 구현해보겠습니다.본 포스팅에서 python 패키지 매니저로는 uv를 사용하겠습니다.식사 메뉴를 추천해주는 MCP Tool 입니다. FastMCP는 type hint와 docstring을 기반으로 자동으로 스키마를 생성하는데요,이 구조 덕분에 LLM Host가 Tool을 정확하게 이해하고 호출할 수 있습니다.따라서 함수의 파라미터에 명확한 타입 지정과 상세한 설명(description) 을 적어주는 것이 중요합니다.이제 구현한 MCP Server를 실제로 테스트해보기 위해, Claude에 연결해보겠습니다.Claude Desktop을 이용해 LLM이 MCP Tool을 어떻게 호출하는지 확인해볼 수 있습니다.Claude -> 설정 -> 개발자 메뉴에 들어가면 MCP Tool을 등록하는 화면이 있습니다. '설정 편집'을 누르고 claude_desktop_config.json을 수정합니다.해당 설정 파일에 들어갈 내용은 다음과 같습니다. command는 MCP Server를 실행할 명령입니다. args에는 실행에 필요한 여러 파라미터를 넣을 수 있습니다.설정을 마친 뒤에는 Claude Desktop 프로그램을 재실행합니다. 정상적으로 연동되었다면, 아래와 같이 Tool 목록에서 앞서 등록한 MCP Tool이 표시되는 것을 확인할 수 있습니다.이제 "점심 메뉴 추천해줘"라고 입력해보겠습니다. 입력을 하면 다음과 같이 Tool 호출 메뉴가 보입니다.Tool 호출 허용을 하면 아래와 같이 앞서 만들었던 MCP Tool이 호출되고 실행된 결과를 LLM이 해석하여 자연어로 표현해줍니다.Claude와 같은 MCP Host를 만들기 전에 Tool을 호출하는 과정부터 진행해보겠습니다.다음은 입력 파라미터로 mcp server 스크립트 경로를 받아 MCP Client를 동작시키는 코드입니다.다음과 같이 실행하면 입력한 경로의 mcp server 스크립트를 연결하여 Tool List를 출력하는 것을 확인할 수 있습니다.한 단계 더 나아가
python
6/4/2025
logo
USB-C처럼 연결되는 AI 인터페이스, MCP Server를 만들어보자
MCP(Model Context Protocol)는 현재 기준으로 사실상 AI 모델 연동의 표준처럼 사용되고 있는 것 같습니다.2024년 11월, Anthropic이 처음 공개한 이후 빠르게 주목을 받으며 성장세를 이어가고 있는데요.아직 발전 중인 단계이지만, JSON-RPC 기반의 심플한 구조와 다양한 SDK, 프레임워크 제공 덕분에 개발자들에게 큰 관심을 받고 있습니다.MCP는 공식 소개 문서에서도 언급되듯이, ‘AI의 USB-C’로 비유되곤 합니다.하나의 단일 인터페이스로 다양한 기기와 연결할 수 있는 USB-C처럼, MCP 역시 LLM이 다양한 외부 시스템(도구, 데이터 소스 등)과 일관된 방식으로 통신할 수 있게 해줍니다.조금 다른 이야기이지만, 저는 이 개념이 기존의 운영체제(OS) 구조와도 맞닿아 있다고 생각합니다.우리가 사용하는 OS, 특히 리눅스는 디바이스 드라이버를 파일처럼 추상화하여 상호작용하죠.그리고 이 구조는 HAL(Hardware Abstraction Layer)이라는 공통 규약을 바탕으로 구성되어 있습니다.이 구조에서 착안하면, LLM과 MCP를 활용해 다양한 외부 시스템(디바이스, API, 내부 도구 등)을 규격화해 제어할 수 있다는 점이 중요합니다.예를 들어, IoT 디바이스의 경우 그동안 제조사마다 각기 다른 제어 인터페이스를 연동해야 했지만,MCP 기반으로 통일된 인터페이스를 제공하면 LLM을 통해 자연어로 손쉽게 제어가 가능합니다.제조사는 자신의 디바이스에 맞는 MCP Server만 제공하면 되는 구조인 것이죠.실제로 엔트로픽의 Claude는 ‘Computer Use’ 기능을 통해 브라우저를 조작하거나 파일을 열고 편집하는 작업까지 수행할 수 있고,OpenAI 역시 ‘Operator’ 개념을 통해 LLM이 외부 기능과 직접 연동되도록 하는 실험을 진행하고 있습니다.이런 흐름을 보면, 조만간 운영체제 자체를 LLM이 직접 제어하는, 완전히 새로운 형태의 OS가 등장할 수 있으리라 기대됩니다.앞으로 어떤 새로운 프로토콜이 등장할지는 알 수 없지만, 공통 인터페이스라는 구조적 장점만으로도 다양한 가능성을 열어줄 수 있다는 점은 분명해 보입니다.그런 점에서 MCP는 LLM 활용에 있어 하나의 강력한 시작점을 제시하며, 많은 개발자와 기업의 관심을 받고 있는 것 같습니다.이번 포스트에서는 Python 기반으로 MCP(Model Context Protocol)를 적용하는 기본적인 코드 구현 방법을 단계적으로 소개해보려 합니다.MCP Server 구현부터, 간단한 커맨드라인 스크립트를 통한 MCP Tool 호출,마지막으로 Streamlit을 활용해 자연어로 MCP Tool을 실행하는 Agent 생성까지 차근차근 따라가며 실습해보겠습니다.MCP의 핵심은 호스트 애플리케이션이 여러 서버에 연결할 수 있는 클라이언트-서버 아키텍처를 따르는 것입니다.• None MCP 호스트: MCP를 통해 데이터에 액세스하려는 클로드 데스크톱, IDE 또는 AI 도구와 같은 프로그램• None MCP 클라이언트: 서버와 1:1 연결을 유지하는 프로토콜 클라이언트• None 로컬 데이터 소스)을 통해 각각 특정 기능을 노출하는 경량 프로그램: MCP 서버가 안전하게 액세스할 수 있는 내 컴퓨터의 파일, 데이터베이스 및 서비스• None 원격 서비스: MCP 서버가 연결할 수 있는 인터넷을 통해 사용 가능한 외부 시스템(예: API를 통해)위 설명에서 클라이언트-서버 연동 구조라고 하여 MCP Server가 항상 어떤 원격지에 있는 것은 아닙니다.MCP Host, Client, Server가 모두 한 PC에서도 실행될 수 있고 Server만 원격지에 존재하는 경우도 있습니다.MCP Server를 로컬 PC에 설치해서 사용 가능한 도구들이 다양하게 존재합니다. (ex, Desktop Commander)먼저, MCP의 기반이 되는 MCP Server를 직접 만들어보겠습니다. Python 구현체인 FastMCP를 활용해 간단한 MCP Server를 구성해볼 예정인데요.이 포스트에서는 복잡한 내부 구조보다는 실행에 초점을 맞춰, 빠르게 테스트해볼 수 있는 최소한의 형태로 구현해보겠습니다.본 포스팅에서 python 패키지 매니저로는 uv를 사용하겠습니다.식사 메뉴를 추천해주는 MCP Tool 입니다. FastMCP는 type hint와 docstring을 기반으로 자동으로 스키마를 생성하는데요,이 구조 덕분에 LLM Host가 Tool을 정확하게 이해하고 호출할 수 있습니다.따라서 함수의 파라미터에 명확한 타입 지정과 상세한 설명(description) 을 적어주는 것이 중요합니다.이제 구현한 MCP Server를 실제로 테스트해보기 위해, Claude에 연결해보겠습니다.Claude Desktop을 이용해 LLM이 MCP Tool을 어떻게 호출하는지 확인해볼 수 있습니다.Claude -> 설정 -> 개발자 메뉴에 들어가면 MCP Tool을 등록하는 화면이 있습니다. '설정 편집'을 누르고 claude_desktop_config.json을 수정합니다.해당 설정 파일에 들어갈 내용은 다음과 같습니다. command는 MCP Server를 실행할 명령입니다. args에는 실행에 필요한 여러 파라미터를 넣을 수 있습니다.설정을 마친 뒤에는 Claude Desktop 프로그램을 재실행합니다. 정상적으로 연동되었다면, 아래와 같이 Tool 목록에서 앞서 등록한 MCP Tool이 표시되는 것을 확인할 수 있습니다.이제 "점심 메뉴 추천해줘"라고 입력해보겠습니다. 입력을 하면 다음과 같이 Tool 호출 메뉴가 보입니다.Tool 호출 허용을 하면 아래와 같이 앞서 만들었던 MCP Tool이 호출되고 실행된 결과를 LLM이 해석하여 자연어로 표현해줍니다.Claude와 같은 MCP Host를 만들기 전에 Tool을 호출하는 과정부터 진행해보겠습니다.다음은 입력 파라미터로 mcp server 스크립트 경로를 받아 MCP Client를 동작시키는 코드입니다.다음과 같이 실행하면 입력한 경로의 mcp server 스크립트를 연결하여 Tool List를 출력하는 것을 확인할 수 있습니다.한 단계 더 나아가
2025.06.04
python
emoji
좋아요
emoji
별로에요
logo
생산성을 높이는 Android SDK 배포 전략 살펴보기
저희 팀에서는 Android SDK를 배포할 때마다 신경 써야 할 부분이 많았습니다. 빌드 타입에 따라 배포해야 할 모듈이나 배포 저장소가 달라지고, 배포 이후에는 일일이 가이드 문서를 업데이트하고 샘플 앱을 최신화하는 등 복잡하거나 번거로운 작업이 많았습니다.그래서 Android SDK 배포 프로세스를 좀 더 효율적이고 생산적으로 만들 수 있는 방법을 고민했고, 현재는 여러 설루션을 도입하여 굉장히 편리하고 안정적으로 배포를 진행하고 있습니다.이 글은 Android SDK 배포 과정을 개선해 더욱 생산적으로 배포할 수 있게 된 경험을 공유하고, 그 과정에서 사용된 기술을 소개합니다.배포를 위한 conventional commit과 GitHub Actions많은 팀이 저장소 안에서 일정한 규칙을 지키며 다양한 이유로 conventional commit을 사용하고 있을 텐데요, 저희 팀은 배포를 위한 conventional commit을 사용하고 있습니다.시맨틱 버저닝에 맞춰서 커밋 유형에 따라 다음 버전이 결정되고, 커밋 메시지가 곧 저희 SDK의 릴리스 노트가 됩니다.예를 들어 8.3.0 버전에서 fix 커밋이 추가되면 패치 버전이 올라가 8.3.1 버전이 되고, feat 커밋이 발생하면 마이너 버전이 올라가 8.4.0 버전이 됩니다. 또, 커밋의 유형뿐만 아니라 메시지도 이용해 저희 SDK를 사용하는 매체에 공개될 릴리스 노트를 만듭니다.이러한 과정의 자동화를 구축할 수 있는 유명한 오픈소스가 있는데요, 바로 google의 Release Please Action입니다. Release Please Action은 GitHub Actions 기반의 프로젝트 배포 보조 툴로, 프로젝트에 적용되면 저장소의 커밋 이력을 conventional commit 기반으로 자동 분석해 버전 정보와 릴리스 노트 등의 배포 정보를 만들고 그에 따른 PR(pull request)까지 자동으로 생성합니다.그래서 SDK와 같이 버저닝이나 릴리스 노트에 신경써야 하는 프로젝트라면 Release Please Action 및 conventional commit을 이용해 생산성을 크게 높일 수 있습니다.가이드 문서 업데이트 자동화저희 팀은 Release Please Action 적용에서 한 단계 더 나아가 추가 자동화 프로세스를 구축했습니다. 바로 가이드 문서 업데이트입니다. 저희 SDK 가이드 문서는 사내용과 외부용으로 나뉘어 총 2개의 저장소를 사용하고 있는데요, 그래서 안 그래도 번거로운 가이드 문서 수정을 두 번이나 해야 했습니다.이를 해결하기 위해 저희는 Release Please Action가 배포 정보를 생성하면, 이를 가이드 문서 저장소에 반영하는 PR을 생성하는 프로세스를 자동화했습니다.이 프로세스에는 GitHub Actions의 repository_dispatch를 활용했습니다. 이를 활용하면 특정 저장소에 정의된 워크플로를 저장소 외부에서 REST API 호출로 실행할 수 있고, event_type이 포함된 미리 정의된 형식의 데이터를 body에 넣어서 다양한 정보를 같이 넘기고 워크플로에서 이 정보를 활용할 수도 있습니다.먼저 SDK 저장소의 워크플로 파일부터 간단하게 살펴보겠습니다. 다음은 워크플로 파일에서 저장소 체크아웃이나 JDK 설정 등의 기본적인 작업은 생략하고 자동화와 관련된 부분만 간단하게 추린 코드입니다.on: push: branches: - mainname: Release SDKjobs: release: runs-on: ubuntu-latest steps: - name: Release please action uses: googleapis/release-please-action@v4 id: release with: token: ${{ secrets.RELEASE_PLEASE_TOKEN }} - name: Update guide repository if: steps.release.outputs.releases_created == 'true' env: json: ${{ toJSON(steps.release.outputs) }} run: | curl -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer " \ https://api.github.com/repos/android-sdk/guide/dispatches \ -d '{"event_type":"release-pr","client_payload":{"release_outputs":${{json}}}'먼저, Release Please Action이 생성한 결과에 접근해서 배포 정보가 생성되었는지 확인하고 생성되었다면 관련 정보를 가져옵니다. 그리고 앞에서 언급한 repository_dispatch API에 해당 정보를 담아서 가이드 문서 저장소에 호출합니다.event_type은 임의로 release-pr이라고 지정했고, client_payload 안의 release_outputs이라는 필드에 Release Please Action으로 생성한 정보를 넘기게끔 했습니다.이제 가이드 문서 저장소의 워크플로를 살펴보겠습니다.on: repository_dispatch: types: [release-pr]name: Create new release PRjobs: new-release-pr: runs-on: ubuntu-latest steps: - name: Update version run: | new_version=${{ github.event.client_payload.release_outputs.version }} version_path="config.toml" sed -i "s@version = \".*\"@version = \"${new_version}\"@g" "${version_path}" - name: Update release note run: | new_release_note=${{ github.event.client_payload.release_outputs.body }} release_note_path="docs/rele
github
githubaction
java
kotlin
6/4/2025
logo
생산성을 높이는 Android SDK 배포 전략 살펴보기
저희 팀에서는 Android SDK를 배포할 때마다 신경 써야 할 부분이 많았습니다. 빌드 타입에 따라 배포해야 할 모듈이나 배포 저장소가 달라지고, 배포 이후에는 일일이 가이드 문서를 업데이트하고 샘플 앱을 최신화하는 등 복잡하거나 번거로운 작업이 많았습니다.그래서 Android SDK 배포 프로세스를 좀 더 효율적이고 생산적으로 만들 수 있는 방법을 고민했고, 현재는 여러 설루션을 도입하여 굉장히 편리하고 안정적으로 배포를 진행하고 있습니다.이 글은 Android SDK 배포 과정을 개선해 더욱 생산적으로 배포할 수 있게 된 경험을 공유하고, 그 과정에서 사용된 기술을 소개합니다.배포를 위한 conventional commit과 GitHub Actions많은 팀이 저장소 안에서 일정한 규칙을 지키며 다양한 이유로 conventional commit을 사용하고 있을 텐데요, 저희 팀은 배포를 위한 conventional commit을 사용하고 있습니다.시맨틱 버저닝에 맞춰서 커밋 유형에 따라 다음 버전이 결정되고, 커밋 메시지가 곧 저희 SDK의 릴리스 노트가 됩니다.예를 들어 8.3.0 버전에서 fix 커밋이 추가되면 패치 버전이 올라가 8.3.1 버전이 되고, feat 커밋이 발생하면 마이너 버전이 올라가 8.4.0 버전이 됩니다. 또, 커밋의 유형뿐만 아니라 메시지도 이용해 저희 SDK를 사용하는 매체에 공개될 릴리스 노트를 만듭니다.이러한 과정의 자동화를 구축할 수 있는 유명한 오픈소스가 있는데요, 바로 google의 Release Please Action입니다. Release Please Action은 GitHub Actions 기반의 프로젝트 배포 보조 툴로, 프로젝트에 적용되면 저장소의 커밋 이력을 conventional commit 기반으로 자동 분석해 버전 정보와 릴리스 노트 등의 배포 정보를 만들고 그에 따른 PR(pull request)까지 자동으로 생성합니다.그래서 SDK와 같이 버저닝이나 릴리스 노트에 신경써야 하는 프로젝트라면 Release Please Action 및 conventional commit을 이용해 생산성을 크게 높일 수 있습니다.가이드 문서 업데이트 자동화저희 팀은 Release Please Action 적용에서 한 단계 더 나아가 추가 자동화 프로세스를 구축했습니다. 바로 가이드 문서 업데이트입니다. 저희 SDK 가이드 문서는 사내용과 외부용으로 나뉘어 총 2개의 저장소를 사용하고 있는데요, 그래서 안 그래도 번거로운 가이드 문서 수정을 두 번이나 해야 했습니다.이를 해결하기 위해 저희는 Release Please Action가 배포 정보를 생성하면, 이를 가이드 문서 저장소에 반영하는 PR을 생성하는 프로세스를 자동화했습니다.이 프로세스에는 GitHub Actions의 repository_dispatch를 활용했습니다. 이를 활용하면 특정 저장소에 정의된 워크플로를 저장소 외부에서 REST API 호출로 실행할 수 있고, event_type이 포함된 미리 정의된 형식의 데이터를 body에 넣어서 다양한 정보를 같이 넘기고 워크플로에서 이 정보를 활용할 수도 있습니다.먼저 SDK 저장소의 워크플로 파일부터 간단하게 살펴보겠습니다. 다음은 워크플로 파일에서 저장소 체크아웃이나 JDK 설정 등의 기본적인 작업은 생략하고 자동화와 관련된 부분만 간단하게 추린 코드입니다.on: push: branches: - mainname: Release SDKjobs: release: runs-on: ubuntu-latest steps: - name: Release please action uses: googleapis/release-please-action@v4 id: release with: token: ${{ secrets.RELEASE_PLEASE_TOKEN }} - name: Update guide repository if: steps.release.outputs.releases_created == 'true' env: json: ${{ toJSON(steps.release.outputs) }} run: | curl -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer " \ https://api.github.com/repos/android-sdk/guide/dispatches \ -d '{"event_type":"release-pr","client_payload":{"release_outputs":${{json}}}'먼저, Release Please Action이 생성한 결과에 접근해서 배포 정보가 생성되었는지 확인하고 생성되었다면 관련 정보를 가져옵니다. 그리고 앞에서 언급한 repository_dispatch API에 해당 정보를 담아서 가이드 문서 저장소에 호출합니다.event_type은 임의로 release-pr이라고 지정했고, client_payload 안의 release_outputs이라는 필드에 Release Please Action으로 생성한 정보를 넘기게끔 했습니다.이제 가이드 문서 저장소의 워크플로를 살펴보겠습니다.on: repository_dispatch: types: [release-pr]name: Create new release PRjobs: new-release-pr: runs-on: ubuntu-latest steps: - name: Update version run: | new_version=${{ github.event.client_payload.release_outputs.version }} version_path="config.toml" sed -i "s@version = \".*\"@version = \"${new_version}\"@g" "${version_path}" - name: Update release note run: | new_release_note=${{ github.event.client_payload.release_outputs.body }} release_note_path="docs/rele
2025.06.04
github
githubaction
java
kotlin
emoji
좋아요
emoji
별로에요
logo
FE News 25년 6월 소식을 전해드립니다!
주요소식25년 6월 소식에서는 다음과 같은 유용한 정보들을 만나보실 수 있습니다.링크 & 읽을거리The History of JavaScript: 1995년 Brendan Eich가 10일만에 설계한 언어부터 현대 JavaScript 생태계까지 진화 과정을 시간순으로 정리한 글입니다.Google I/O 2025 웹 업데이트: CSS만으로 구현 가능한 캐러셀, Interest Invoker API, Gemini 기반 AI 및 Chrome DevTools AI 어시스턴트 등 10가지 주요 업데이트가 소개되었습니다.V8 엔진 기능 강화: 명시적 자원 관리(Explicit Resource Management)와 컴파일 힌트(Explicit Compile Hints) 기능이 추가되어 성능과 자원 관리가 크게 개선되었습니다.Deno의 하락세: 출시 당시 Node.js의 대안으로 기대를 모았던 Deno가 6개 주요 지역에서 관심도와 사용률이 지속적으로 감소하고 있는 현상을 데이터 기반으로 분석한 글입니다.튜토리얼AI 기반 E2E UI 테스팅: AI를 활용해 UI 테스트의 생성과 유지보수를 더 쉽게 만드는 방법을 소개하는 튜토리얼입니다.함수형 프로그래밍 개념: JavaScript에서 재귀적 데이터 구조와 지연 평가(lazy evaluation)를 활용하는 심층 기술 문서입니다.JavaScript 개발자를 위한 Go 가이드: JavaScript와 Go의 주요 차이점과 유사점을 비교 설명하는 가이드입니다.코드와 도구Rolldown-Vite 출시: Evan You가 Rollup의 Rust 구현체인 Rolldown을 공개했습니다. JavaScript 대신 Rust로 재구현하여 최대 10배 빠른 성능을 제공하며, 향후 Vite의 기본 번들러로 채택될 예정입니다.react-sounds: React 애플리케이션에 고품질 오디오 경험을 쉽게 통합할 수 있는 새로운 라이브러리로, 웹 오디오 API의 복잡성을 추상화했습니다.stagewise: AI 코드 에디터를 위한 시각적 도우미로, GitHub Copilot 등의 AI 코딩 도구를 더 효과적으로 활용할 수 있게 도와줍니다.excelize-wasm: 인기 Go 기반 Excel 처리 라이브러리의 WebAssembly 포팅 버전으로, 브라우저에서 직접 Excel 파일을 처리할 수 있습니다.>> FE News 25년 6월 소식 보러가기 FE News란? 네이버 FE 엔지니어들이 엄선한 양질의 FE 및 주요한 기술 소식들을 큐레이션해 공유하는 것을 목표로 하며, 이를 통해 국내 개발자들에게 지식 공유에 대한 가치 인식과 성장에 도움을 주고자 하는 기술소식 공유 프로젝트 입니다. 매월 첫째 주 수요일, 월 1회 발행 되고 있으니 많은 관심 부탁드립니다. 구독하기
javascript
6/4/2025
logo
FE News 25년 6월 소식을 전해드립니다!
주요소식25년 6월 소식에서는 다음과 같은 유용한 정보들을 만나보실 수 있습니다.링크 & 읽을거리The History of JavaScript: 1995년 Brendan Eich가 10일만에 설계한 언어부터 현대 JavaScript 생태계까지 진화 과정을 시간순으로 정리한 글입니다.Google I/O 2025 웹 업데이트: CSS만으로 구현 가능한 캐러셀, Interest Invoker API, Gemini 기반 AI 및 Chrome DevTools AI 어시스턴트 등 10가지 주요 업데이트가 소개되었습니다.V8 엔진 기능 강화: 명시적 자원 관리(Explicit Resource Management)와 컴파일 힌트(Explicit Compile Hints) 기능이 추가되어 성능과 자원 관리가 크게 개선되었습니다.Deno의 하락세: 출시 당시 Node.js의 대안으로 기대를 모았던 Deno가 6개 주요 지역에서 관심도와 사용률이 지속적으로 감소하고 있는 현상을 데이터 기반으로 분석한 글입니다.튜토리얼AI 기반 E2E UI 테스팅: AI를 활용해 UI 테스트의 생성과 유지보수를 더 쉽게 만드는 방법을 소개하는 튜토리얼입니다.함수형 프로그래밍 개념: JavaScript에서 재귀적 데이터 구조와 지연 평가(lazy evaluation)를 활용하는 심층 기술 문서입니다.JavaScript 개발자를 위한 Go 가이드: JavaScript와 Go의 주요 차이점과 유사점을 비교 설명하는 가이드입니다.코드와 도구Rolldown-Vite 출시: Evan You가 Rollup의 Rust 구현체인 Rolldown을 공개했습니다. JavaScript 대신 Rust로 재구현하여 최대 10배 빠른 성능을 제공하며, 향후 Vite의 기본 번들러로 채택될 예정입니다.react-sounds: React 애플리케이션에 고품질 오디오 경험을 쉽게 통합할 수 있는 새로운 라이브러리로, 웹 오디오 API의 복잡성을 추상화했습니다.stagewise: AI 코드 에디터를 위한 시각적 도우미로, GitHub Copilot 등의 AI 코딩 도구를 더 효과적으로 활용할 수 있게 도와줍니다.excelize-wasm: 인기 Go 기반 Excel 처리 라이브러리의 WebAssembly 포팅 버전으로, 브라우저에서 직접 Excel 파일을 처리할 수 있습니다.>> FE News 25년 6월 소식 보러가기 FE News란? 네이버 FE 엔지니어들이 엄선한 양질의 FE 및 주요한 기술 소식들을 큐레이션해 공유하는 것을 목표로 하며, 이를 통해 국내 개발자들에게 지식 공유에 대한 가치 인식과 성장에 도움을 주고자 하는 기술소식 공유 프로젝트 입니다. 매월 첫째 주 수요일, 월 1회 발행 되고 있으니 많은 관심 부탁드립니다. 구독하기
2025.06.04
javascript
emoji
좋아요
emoji
별로에요
logo
어서와요 XR의 숲 : Android Jetpack XR SDK 활용기
2025 Google I/O에서 Android XR Glasses의 데모를 공개했습니다.실시간으로 상대방의 말을 번역하여 소통의 허들을 낮추고, 원하는 장소로 갈 수 있게끔 사용자의 시야에 따라 방향과 설명을 제공합니다.사용자가 입력한 텍스트를 넘어서 XR과 Gemini를 결합하여 AI가 사용자가 보고 있는 것, 듣고 있는 것을 바탕으로 정보를 제공하는 미래가 성큼 다가왔음을 느낄 수 있었습니다.그래서! 오늘은 에 대해 알아보고, 에이닷 앱에 XR을 적용해 본 경험을 공유하고자 합니다.• None Android XR에서 제공하는 개발 옵션 중 를 통한 개발 방법에 대한 글임을 밝힙니다.• None Jetpack XR SDK 중 ARCore, Scenecore로 3D 모델 구현에 관한 내용은 제외하였으니 참고하시길 바랍니다.• None• None 가장 최신 버전의 Canary에서만 가능하니 스튜디오 버전을 확인하시길 바랍니다.• None• None 매번 다르지만 10분 이내로 빌드되었습니다.• None 가장 마지막으로 release 된 라이브러리 버전을 기준으로 작업했습니다.• None 라이브러리가 지속적으로 업데이트 되고 있으니 공식문서를 꼭 확인하시길 바랍니다.• None alpha04를 적용하면서 XrExtensions 관련 에러가 있었습니다.• None 에러 발생 파일인 JxrPlatformAdapterAxr.java 파일을 보니 import 하는 라이브러리가 존재하지 않았고 아래 libs.versions.toml 파일에 맨 아래 2줄을 추가하여 해결했습니다.• None XrExtensions는 scenecore-testing 라이브러리에 존재합니다.공식 문서의 내용을 하나씩 이해하고 체험하기 위해 새로운 프로젝트를 만들어서 확인해 보았습니다.Android Studio > New Project를 누르면 아래 화면이 나오는데, 좌측 메뉴에 'XR'이 있습니다. 선택하여 프로젝트 이름 제외 아무 설정도 바꾸지 않고 생성했습니다.XR에는 Home Space, Full Space 두 가지 모드가 있습니다. 공간화된 앱을 테스트하고 싶다면 Full Space로 두어 확인을 해야 합니다.별다른 설정을 하지 않으면 앱을 실행하자마자 Home Space로 보이는데, 이를 바꾸고 싶다면 AndroidManifest 파일에서 아래와 같이 변경할 수 있습니다.그리고 지금 Android XR 기기가 출시되지 않았기 때문에 XR Device Emulator를 꼭 설치해야 테스트할 수 있습니다.New Project 눌렀을 때와 동일하게 좌측 메뉴에 'XR'을 선택할 수 있습니다. 선택 후 수정 없이 Finish 버튼을 눌렀습니다.Emulator를 실행하게 되면 간혹 아래 왼쪽 그림처럼 Android Studio의 Device Manager 창 내에서 Emulator가 열립니다.이 상태에선 조작하기 불편하므로 수정이 필요합니다. Android Studio의 Settings > Tools > Emulator에서 'Launch in the Running Devices tool window' 항목의 체크박스를 풀어줍니다.그러면 가장 오른쪽 사진처럼 Emulator가 별개의 창으로 열립니다.프로젝트 새로 생성 시 있는 기본 코드를 바탕으로 테스트했습니다.Android XR에서 제공하는 UI Component는 크게 2개로 나뉩니다.는 XR 환경에서 3D UI를 담는 Container 역할을 합니다. Full Space 에서만 rendering 되며, 코드 내 어느 곳에든 배치할 수 있습니다.Subspace 중 특별하게 가장 최상위에 두어야 하는 ApplicationSubspace이 있습니다.ApplicationSubspace는 그 어떤 것에도 nested 될 수 없습니다. 무조건 Subspace 중 최상위에 있어야 하며 다른 Subspace에 nest 되면 아래와 같은 에러가 발생합니다.그래서 ApplicationSubspace는 앱 전체를 XR 환경에 둘 때 사용하고, Subspace는 코드 내 어디에든 둘 수 있어서 앱 전체 또는 일부만 XR 환경에 둘 때 용이합니다.: Subspace Composables는 Subspace 내에서만 위치 가능합니다.은 Subspace 내 가장 기본이 되는 요소이며 2D UI를 담는 Container 역할을 합니다.• None SpatialPanel은 여러 개를 만들 수 있고 가로 혹은 세로로 Panel을 배치하고 싶다면 SpatialBox/Row/Column을 사용하여 나타낼 수 있습니다.• None SpatialPanel은 Jetpack Compose에서 쓰던 Modifier와 조금 다른 SubspaceModifier를 사용합니다.• None SubspaceModifier에는 resizable, movable 이란 속성이 있는데 이는 XR 환경에서 Panel의 사이즈를 조절할 수 있고 위치를 옮길 수 있도록 합니다.• None resizable과 movable을 두는 순서가 중요한데 resizable이 된 채로 이동하면 panel의 위칫값에 영향을 줄 수 있으니, movable을 먼저 쓰고 그 뒤에 resizable을 써서 테스트를 진행했습니다.만약 Subspace 없이 SpatialPanel을 실행하면 아래와 같은 'ClassCastException' 에러가 발생합니다.: Spatialized Composable은 2D Layout과 함께 위치할 수 있으며 Full Space 일 경우에만 3D로 rendering 됩니다.는 Floating 요소입니다.앱 화면에서 사용자가 제어할 수 있는 요소를 두어 정돈된 느낌을 주고 사용자가 제어할 수 있는 것들에 더 빠르게 접근할 수 있게 해줍니다.저는 Orbiter에 아래 4개 버튼을 두고 테스트했습니다.Orbiter는 Spatialized Composables 라서 2D Layout 과 같이 배치될 수 있는데, 각 Space에서 Orbiter는 어떻게 보일까요?코드는 아래와 같습니다. Home Space 일 때 실행되는 함수 My2DContent 내에 SpaceOrbiter와 Text를 가로로 배치했습니다.그리고
6/4/2025
logo
어서와요 XR의 숲 : Android Jetpack XR SDK 활용기
2025 Google I/O에서 Android XR Glasses의 데모를 공개했습니다.실시간으로 상대방의 말을 번역하여 소통의 허들을 낮추고, 원하는 장소로 갈 수 있게끔 사용자의 시야에 따라 방향과 설명을 제공합니다.사용자가 입력한 텍스트를 넘어서 XR과 Gemini를 결합하여 AI가 사용자가 보고 있는 것, 듣고 있는 것을 바탕으로 정보를 제공하는 미래가 성큼 다가왔음을 느낄 수 있었습니다.그래서! 오늘은 에 대해 알아보고, 에이닷 앱에 XR을 적용해 본 경험을 공유하고자 합니다.• None Android XR에서 제공하는 개발 옵션 중 를 통한 개발 방법에 대한 글임을 밝힙니다.• None Jetpack XR SDK 중 ARCore, Scenecore로 3D 모델 구현에 관한 내용은 제외하였으니 참고하시길 바랍니다.• None• None 가장 최신 버전의 Canary에서만 가능하니 스튜디오 버전을 확인하시길 바랍니다.• None• None 매번 다르지만 10분 이내로 빌드되었습니다.• None 가장 마지막으로 release 된 라이브러리 버전을 기준으로 작업했습니다.• None 라이브러리가 지속적으로 업데이트 되고 있으니 공식문서를 꼭 확인하시길 바랍니다.• None alpha04를 적용하면서 XrExtensions 관련 에러가 있었습니다.• None 에러 발생 파일인 JxrPlatformAdapterAxr.java 파일을 보니 import 하는 라이브러리가 존재하지 않았고 아래 libs.versions.toml 파일에 맨 아래 2줄을 추가하여 해결했습니다.• None XrExtensions는 scenecore-testing 라이브러리에 존재합니다.공식 문서의 내용을 하나씩 이해하고 체험하기 위해 새로운 프로젝트를 만들어서 확인해 보았습니다.Android Studio > New Project를 누르면 아래 화면이 나오는데, 좌측 메뉴에 'XR'이 있습니다. 선택하여 프로젝트 이름 제외 아무 설정도 바꾸지 않고 생성했습니다.XR에는 Home Space, Full Space 두 가지 모드가 있습니다. 공간화된 앱을 테스트하고 싶다면 Full Space로 두어 확인을 해야 합니다.별다른 설정을 하지 않으면 앱을 실행하자마자 Home Space로 보이는데, 이를 바꾸고 싶다면 AndroidManifest 파일에서 아래와 같이 변경할 수 있습니다.그리고 지금 Android XR 기기가 출시되지 않았기 때문에 XR Device Emulator를 꼭 설치해야 테스트할 수 있습니다.New Project 눌렀을 때와 동일하게 좌측 메뉴에 'XR'을 선택할 수 있습니다. 선택 후 수정 없이 Finish 버튼을 눌렀습니다.Emulator를 실행하게 되면 간혹 아래 왼쪽 그림처럼 Android Studio의 Device Manager 창 내에서 Emulator가 열립니다.이 상태에선 조작하기 불편하므로 수정이 필요합니다. Android Studio의 Settings > Tools > Emulator에서 'Launch in the Running Devices tool window' 항목의 체크박스를 풀어줍니다.그러면 가장 오른쪽 사진처럼 Emulator가 별개의 창으로 열립니다.프로젝트 새로 생성 시 있는 기본 코드를 바탕으로 테스트했습니다.Android XR에서 제공하는 UI Component는 크게 2개로 나뉩니다.는 XR 환경에서 3D UI를 담는 Container 역할을 합니다. Full Space 에서만 rendering 되며, 코드 내 어느 곳에든 배치할 수 있습니다.Subspace 중 특별하게 가장 최상위에 두어야 하는 ApplicationSubspace이 있습니다.ApplicationSubspace는 그 어떤 것에도 nested 될 수 없습니다. 무조건 Subspace 중 최상위에 있어야 하며 다른 Subspace에 nest 되면 아래와 같은 에러가 발생합니다.그래서 ApplicationSubspace는 앱 전체를 XR 환경에 둘 때 사용하고, Subspace는 코드 내 어디에든 둘 수 있어서 앱 전체 또는 일부만 XR 환경에 둘 때 용이합니다.: Subspace Composables는 Subspace 내에서만 위치 가능합니다.은 Subspace 내 가장 기본이 되는 요소이며 2D UI를 담는 Container 역할을 합니다.• None SpatialPanel은 여러 개를 만들 수 있고 가로 혹은 세로로 Panel을 배치하고 싶다면 SpatialBox/Row/Column을 사용하여 나타낼 수 있습니다.• None SpatialPanel은 Jetpack Compose에서 쓰던 Modifier와 조금 다른 SubspaceModifier를 사용합니다.• None SubspaceModifier에는 resizable, movable 이란 속성이 있는데 이는 XR 환경에서 Panel의 사이즈를 조절할 수 있고 위치를 옮길 수 있도록 합니다.• None resizable과 movable을 두는 순서가 중요한데 resizable이 된 채로 이동하면 panel의 위칫값에 영향을 줄 수 있으니, movable을 먼저 쓰고 그 뒤에 resizable을 써서 테스트를 진행했습니다.만약 Subspace 없이 SpatialPanel을 실행하면 아래와 같은 'ClassCastException' 에러가 발생합니다.: Spatialized Composable은 2D Layout과 함께 위치할 수 있으며 Full Space 일 경우에만 3D로 rendering 됩니다.는 Floating 요소입니다.앱 화면에서 사용자가 제어할 수 있는 요소를 두어 정돈된 느낌을 주고 사용자가 제어할 수 있는 것들에 더 빠르게 접근할 수 있게 해줍니다.저는 Orbiter에 아래 4개 버튼을 두고 테스트했습니다.Orbiter는 Spatialized Composables 라서 2D Layout 과 같이 배치될 수 있는데, 각 Space에서 Orbiter는 어떻게 보일까요?코드는 아래와 같습니다. Home Space 일 때 실행되는 함수 My2DContent 내에 SpaceOrbiter와 Text를 가로로 배치했습니다.그리고
2025.06.04
emoji
좋아요
emoji
별로에요
logo
코드 품질 개선 기법 14편: 책임을 부여하는 오직 하나의 책임
안녕하세요. 커뮤니케이션 앱 LINE의 모바일 클라이언트를 개발하고 있는 Ishikawa입니다.저희 회사는 높은 개발 생산성을 유지하기 위해 코드 품질 및 개발 문화 개선에 힘쓰고 있습니다. 이를 위해 다양한 노력을 하고 있는데요. 그중 하나가 Review Committee 활동입니다.Review Committee에서는 머지된 코드를 다시 리뷰해 리뷰어와 작성자에게 피드백을 주고, 리뷰하면서 얻은 지식과 인사이트를 Weekly Report라는 이름으로 매주 공유하고 있습니다. 이 Weekly Report 중 일반적으로 널리 적용할 수 있는 주제를 골라 블로그에 코드 품질 개선 기법 시리즈를 연재하고 있습니다.이번에 블로그로 공유할 Weekly Report의 제목은 '책임을 부여하는 오직 하나의 책임'입니다.책임을 부여하는 오직 하나의 책임불꽃놀이, 로켓, 프로덕트 중 하나를 론치(launch)하는 '론치 버튼'을 구현하고 싶다고 가정해 봅시다. 이 론치 버튼은 '눌렀을 때 무엇을 실행할 것인가'를 동적으로 변경해야 합니다.다음 는 론치할 로직 를 생성자 인수로 받습니다. 그리고 버튼을 눌렀을 때 적절한 가 실행되도록 에서 이벤트 리스너를 등록합니다.이 바인더 클래스는 충분히 단순하지만, '버튼이 눌렸을 때 로직을 바인딩하는 책임'과 '어떤 가 유효한지 결정하는 책임'의 두 가지 책임이 있습니다. 이를 개선하기 위해 가 하나의 와 바인딩되도록 변경하고 를 선택하는 기능은 라는 별도 클래스로 분리 구현하기로 결정했습니다.아래 코드에서는 인스턴스가 생성될 때 각 별로 인스턴스가 생성됩니다. 각 에서 이벤트 리스너를 등록하기 때문에 버튼에 등록되는 리스너는 총 세 개입니다. 세 개의 리스너 중 어떤 리스너를 실행할지 제어하기 위해 라는 속성을 추가했습니다.이렇게 해서 버튼에 대한 로직 구현과 의 책임을 분리할 수 있었습니다. 하지만 오히려 새로운 문제도 발생했습니다. 어떤 문제일까요?새로운 문제점 중 하나는 '사양의 제약 조건을 보장하는 책임이 분산된다'는 점입니다. 변경 전 코드에서는 버튼을 눌렀을 때 셋 중 오직 한 가지만 실행된다는 제약 조건이 있었습니다. 새로운 코드에서도 , , 의 조합을 통해 이를 구현했지만, 이것이 올바른지 확인하려면 모든 코드를 읽어야 합니다. 이 문제는 '어떤 가 선택됐는지'를 나타내는 속성을 에 추가해 해결할 수 있지만, 이렇게 하면 이번에는 추가한 속성과 사이에 상태 중복이 발생합니다.또한 세부 사항 은닉과 관련된 문제도 있습니다. 분리된 코드에서 는 를 선택하는 것만 담당하도록 설계하고자 했습니다. 그러나 인스턴스를 생성하려면 에 직접 의존해야 합니다. 이를 개선하기 위해 를 로부터 숨기도록 아래와 같이 의 인스턴스를 직접 보유하는 방법도 생각해 볼 수 있습니다.하지만 이렇게 되면 호출자가 모든 의존성( , , , )을 해결해야 하기 때문에 결과적으로 호출자가 비대해지거나(소위 God class) 전체 그림을 파악하기 위해 수많은 클래스를 읽어야 하는 경우가 발생하기도(소위 Ravioli code) 합니다.책임을 고려하는 책임의 초기 구현은 사양의 제약 조건을 한 곳에서 구현했다는 장점이 있기 때문에 종합적으로 생각해 봤을 때 더 이상 클래스를 분할할 필요가 없었을 수도 있습니다. 클래스를 분할할 때는 '개별 클래스의 책임 범위나 응집도에 지나치게 집중한 나머지 호출자의 책임이 비대해지거나 클래스 및 모듈 간의 의존성과 결합도가 악화되는 것을 피해야 한다'는 것을 고려해야 합니다.
6/4/2025
logo
코드 품질 개선 기법 14편: 책임을 부여하는 오직 하나의 책임
안녕하세요. 커뮤니케이션 앱 LINE의 모바일 클라이언트를 개발하고 있는 Ishikawa입니다.저희 회사는 높은 개발 생산성을 유지하기 위해 코드 품질 및 개발 문화 개선에 힘쓰고 있습니다. 이를 위해 다양한 노력을 하고 있는데요. 그중 하나가 Review Committee 활동입니다.Review Committee에서는 머지된 코드를 다시 리뷰해 리뷰어와 작성자에게 피드백을 주고, 리뷰하면서 얻은 지식과 인사이트를 Weekly Report라는 이름으로 매주 공유하고 있습니다. 이 Weekly Report 중 일반적으로 널리 적용할 수 있는 주제를 골라 블로그에 코드 품질 개선 기법 시리즈를 연재하고 있습니다.이번에 블로그로 공유할 Weekly Report의 제목은 '책임을 부여하는 오직 하나의 책임'입니다.책임을 부여하는 오직 하나의 책임불꽃놀이, 로켓, 프로덕트 중 하나를 론치(launch)하는 '론치 버튼'을 구현하고 싶다고 가정해 봅시다. 이 론치 버튼은 '눌렀을 때 무엇을 실행할 것인가'를 동적으로 변경해야 합니다.다음 는 론치할 로직 를 생성자 인수로 받습니다. 그리고 버튼을 눌렀을 때 적절한 가 실행되도록 에서 이벤트 리스너를 등록합니다.이 바인더 클래스는 충분히 단순하지만, '버튼이 눌렸을 때 로직을 바인딩하는 책임'과 '어떤 가 유효한지 결정하는 책임'의 두 가지 책임이 있습니다. 이를 개선하기 위해 가 하나의 와 바인딩되도록 변경하고 를 선택하는 기능은 라는 별도 클래스로 분리 구현하기로 결정했습니다.아래 코드에서는 인스턴스가 생성될 때 각 별로 인스턴스가 생성됩니다. 각 에서 이벤트 리스너를 등록하기 때문에 버튼에 등록되는 리스너는 총 세 개입니다. 세 개의 리스너 중 어떤 리스너를 실행할지 제어하기 위해 라는 속성을 추가했습니다.이렇게 해서 버튼에 대한 로직 구현과 의 책임을 분리할 수 있었습니다. 하지만 오히려 새로운 문제도 발생했습니다. 어떤 문제일까요?새로운 문제점 중 하나는 '사양의 제약 조건을 보장하는 책임이 분산된다'는 점입니다. 변경 전 코드에서는 버튼을 눌렀을 때 셋 중 오직 한 가지만 실행된다는 제약 조건이 있었습니다. 새로운 코드에서도 , , 의 조합을 통해 이를 구현했지만, 이것이 올바른지 확인하려면 모든 코드를 읽어야 합니다. 이 문제는 '어떤 가 선택됐는지'를 나타내는 속성을 에 추가해 해결할 수 있지만, 이렇게 하면 이번에는 추가한 속성과 사이에 상태 중복이 발생합니다.또한 세부 사항 은닉과 관련된 문제도 있습니다. 분리된 코드에서 는 를 선택하는 것만 담당하도록 설계하고자 했습니다. 그러나 인스턴스를 생성하려면 에 직접 의존해야 합니다. 이를 개선하기 위해 를 로부터 숨기도록 아래와 같이 의 인스턴스를 직접 보유하는 방법도 생각해 볼 수 있습니다.하지만 이렇게 되면 호출자가 모든 의존성( , , , )을 해결해야 하기 때문에 결과적으로 호출자가 비대해지거나(소위 God class) 전체 그림을 파악하기 위해 수많은 클래스를 읽어야 하는 경우가 발생하기도(소위 Ravioli code) 합니다.책임을 고려하는 책임의 초기 구현은 사양의 제약 조건을 한 곳에서 구현했다는 장점이 있기 때문에 종합적으로 생각해 봤을 때 더 이상 클래스를 분할할 필요가 없었을 수도 있습니다. 클래스를 분할할 때는 '개별 클래스의 책임 범위나 응집도에 지나치게 집중한 나머지 호출자의 책임이 비대해지거나 클래스 및 모듈 간의 의존성과 결합도가 악화되는 것을 피해야 한다'는 것을 고려해야 합니다.
2025.06.04
emoji
좋아요
emoji
별로에요
logo
Next.js 트러블슈팅: CORS와 Version Skew 에러 원인부터 해결까지
john.bosco 웹 애플리케이션을 개발한다면 굉장히 익숙한, cors 라는 키워드에 대한 트러블 슈팅을 다룹니다. cors 라는 키워드만 보면 내용이 단순할것 같지만, 실무에서 겪을 수 있는 일정 문제와 사내 개발환경의 특성 안에서 적절한 해결방안을 찾아가는 흐름에 초점을 두신다면, 글을 더욱 재미있게 읽을 수 있을것 같습니다.larry.charry 글을 읽는 내내 실제 장애를 겪은 것 같은 긴장감이 느껴졌습니다. SSR 프레임워크, 특히 nextjs를 사용한다면 누구나 마주할 수 있는 잠재적 문제들에 대한 값진 해결 과정을 공유하는 경험기라고 생각합니다.안녕하세요, 카카오페이에서 프론트엔드 개발을 하고 있는 라즈입니다.이번 글에서는 머니 충전 서비스 프론트엔드를 개발할 때 겪었던 장애를 공유하고자 합니다. 구체적으로는 Next.js 기반 서비스에서 프론트엔드 개발자에게 익숙한 CORS(Cross-Origin Resource Sharing) 에러와 다소 생소할 수 있는 Version Skew 문제의 원인을 어떻게 파악하고 해결했는지를 설명합니다. 단순히 에러의 기술적인 원인과 해결 방법뿐만 아니라, 문제 해결 과정에서 제가 겪었던 심리적인 압박, 일정 관리와 실패 대비의 중요성까지 여러분들이 간접적으로 경험하실 수 있도록 구성했습니다.Next.js 서버를 직접 운영하는 분들에게 언젠가 참고하거나 문제 해결의 실마리가 되는 글이 되었으면 좋겠습니다.들어가기 앞서 간략하게 머니 충전 서비스(이하 충전)를 소개해드리겠습니다.충전은 카카오페이에 연결된 계좌의 잔액을 가져와 카카오페이 머니로 충전하는 서비스입니다. 기존에 네이티브 앱으로 구현되어 있던 기능을 웹뷰로 전환해야 했기 때문에, 사용자가 최대한 화면을 빠르게 보기 위한 초기 로딩 속도가 중요했습니다. 이를 위해 Next.js(v13, Pages Router) 기반의 SSR 방식을 채택했습니다.충전 서비스의 사용자 플로우는 과 , 두 단계로 비교적 단순합니다. 사용자가 충전을 완료하면, 서버 API 응답으로 받은 충전 결과 데이터를 상태(State)에 저장한 후 완료 화면으로 이동하여 결과를 보여주는 구조입니다.충전 서비스 개발 당시 만약의 상황에 대비하고 타입 추론의 이점을 살리기 위해 충전 완료 페이지에서 충전 결과 상태 값이 비어있는 경우 Sentry로 에러를 로깅하도록 구현해 두었습니다.그런데 QA까지 무사히 마치고 충전 서비스를 배포한 뒤 진입점을 열자마자 해당 Sentry 에러 알람이 폭발적으로 발생하기 시작했습니다. 처음으로 운영 환경에서 마주한 장애 상황이었기 때문에 순간 당황했지만, 즉시 진입점을 롤백하고 원인을 파악하기 시작했습니다.에러의 원인을 찾기 위한 의식의 흐름가장 먼저 에러 발생 빈도를 확인해보니 전체 트래픽 대비 발생한 비율이 상당히 낮았습니다. 아마 이 때문에 QA 단계에서 미처 발견하지 못했던 것 같습니다. 특이한 점은 에러가 한 번 발생하면 순서대로 서버에서 1번, 브라우저에서 3번, 총 4번 발생했고, 재현되지 않는 기기에서는 계속해서 문제가 없었지
nextjs
6/4/2025
logo
Next.js 트러블슈팅: CORS와 Version Skew 에러 원인부터 해결까지
john.bosco 웹 애플리케이션을 개발한다면 굉장히 익숙한, cors 라는 키워드에 대한 트러블 슈팅을 다룹니다. cors 라는 키워드만 보면 내용이 단순할것 같지만, 실무에서 겪을 수 있는 일정 문제와 사내 개발환경의 특성 안에서 적절한 해결방안을 찾아가는 흐름에 초점을 두신다면, 글을 더욱 재미있게 읽을 수 있을것 같습니다.larry.charry 글을 읽는 내내 실제 장애를 겪은 것 같은 긴장감이 느껴졌습니다. SSR 프레임워크, 특히 nextjs를 사용한다면 누구나 마주할 수 있는 잠재적 문제들에 대한 값진 해결 과정을 공유하는 경험기라고 생각합니다.안녕하세요, 카카오페이에서 프론트엔드 개발을 하고 있는 라즈입니다.이번 글에서는 머니 충전 서비스 프론트엔드를 개발할 때 겪었던 장애를 공유하고자 합니다. 구체적으로는 Next.js 기반 서비스에서 프론트엔드 개발자에게 익숙한 CORS(Cross-Origin Resource Sharing) 에러와 다소 생소할 수 있는 Version Skew 문제의 원인을 어떻게 파악하고 해결했는지를 설명합니다. 단순히 에러의 기술적인 원인과 해결 방법뿐만 아니라, 문제 해결 과정에서 제가 겪었던 심리적인 압박, 일정 관리와 실패 대비의 중요성까지 여러분들이 간접적으로 경험하실 수 있도록 구성했습니다.Next.js 서버를 직접 운영하는 분들에게 언젠가 참고하거나 문제 해결의 실마리가 되는 글이 되었으면 좋겠습니다.들어가기 앞서 간략하게 머니 충전 서비스(이하 충전)를 소개해드리겠습니다.충전은 카카오페이에 연결된 계좌의 잔액을 가져와 카카오페이 머니로 충전하는 서비스입니다. 기존에 네이티브 앱으로 구현되어 있던 기능을 웹뷰로 전환해야 했기 때문에, 사용자가 최대한 화면을 빠르게 보기 위한 초기 로딩 속도가 중요했습니다. 이를 위해 Next.js(v13, Pages Router) 기반의 SSR 방식을 채택했습니다.충전 서비스의 사용자 플로우는 과 , 두 단계로 비교적 단순합니다. 사용자가 충전을 완료하면, 서버 API 응답으로 받은 충전 결과 데이터를 상태(State)에 저장한 후 완료 화면으로 이동하여 결과를 보여주는 구조입니다.충전 서비스 개발 당시 만약의 상황에 대비하고 타입 추론의 이점을 살리기 위해 충전 완료 페이지에서 충전 결과 상태 값이 비어있는 경우 Sentry로 에러를 로깅하도록 구현해 두었습니다.그런데 QA까지 무사히 마치고 충전 서비스를 배포한 뒤 진입점을 열자마자 해당 Sentry 에러 알람이 폭발적으로 발생하기 시작했습니다. 처음으로 운영 환경에서 마주한 장애 상황이었기 때문에 순간 당황했지만, 즉시 진입점을 롤백하고 원인을 파악하기 시작했습니다.에러의 원인을 찾기 위한 의식의 흐름가장 먼저 에러 발생 빈도를 확인해보니 전체 트래픽 대비 발생한 비율이 상당히 낮았습니다. 아마 이 때문에 QA 단계에서 미처 발견하지 못했던 것 같습니다. 특이한 점은 에러가 한 번 발생하면 순서대로 서버에서 1번, 브라우저에서 3번, 총 4번 발생했고, 재현되지 않는 기기에서는 계속해서 문제가 없었지
2025.06.04
nextjs
emoji
좋아요
emoji
별로에요
logo
iOS 기기에서의 독립적 AI 실행: MLX 기반 온디바이스 모델
인터넷 없이도 똑똑한 iOS 앱, MLX로 만들기이번 포스팅에서는 iOS 개발 환경에서 주목받고 있는 온디바이스(On-device) AI 모델 실행 방식과,Apple Silicon에 최적화된 머신러닝 프레임워크 MLX에 대해 소개합니다.기술적 배경이 없는 비개발자부터 전문 개발자까지 모두 쉽게 이해할 수 있도록 구성했습니다.온디바이스 AI는 인터넷 연결 없이 스마트폰이나 태블릿과 같은 기기에서직접 인공지능(AI) 모델을 실행하여 데이터를 처리하는 기술입니다.데이터를 외부로 전송하지 않기 때문에 보안 측면에서 우수하며, 빠르고 안정적인 사용자 경험을 제공합니다.온디바이스 AI의 주요 장점데이터가 기기 외부로 전송되지 않기 때문에 개인정보 유출 위험이 낮습니다.네트워크 상태와 무관하게 빠른 응답을 제공할 수 있습니다.오프라인 환경에서도 AI 기능을 사용할 수 있어 활용 범위가 넓습니다.MLX는 Apple Silicon 기반의 기기에서 머신러닝 모델을 효과적으로 실행하기 위해 설계된PyTorch, TensorFlow처럼 배열 기반 연산을 지원하며,특히 Apple 하드웨어(iOS 및 macOS)에서 최적화된 성능을 제공합니다.iPhone, iPad, Mac 등에서 높은 성능과 전력 효율을 제공합니다.모델을 학습하거나 추론하는 작업을 모두 기기 내에서 수행할 수 있습니다.Swift와의 뛰어난 호환성Swift, SwiftUI와 쉽게 통합되어 앱 개발 생산성이 높아집니다.현재 MLX는 다음과 같은 다양한 LLM을 지원하고 있습니다:자세한 지원 모델은 Hugging Face MLX 문서에서 확인할 수 있습니다.SPM을 통해 MLX 라이브러리 추가온디바이스 AI는 성능과 효율 모두 중요합니다. MLX를 활용해 개발할 때 다음 사항을 고려해보세요.• None 모델이 너무 크면 앱 성능이 저하되거나 충돌이 발생할 수 있습니다.• None 가능한 한 경량화된 모델을 선택하고, 양자화(quantization) 또는 프루닝(pruning) 기법을 통해 최적화하세요.• None 경우에 따라 사전 학습 모델을 파인튜닝(fine-tuning) 해 실제 사용 시 연산량을 줄일 수 있습니다.• None 실시간 입력 처리(예: 음성 스트리밍)에서는 버퍼 관리가 핵심입니다.• None 입력/출력 데이터는 정제된 형태로 전달하고, 불필요한 중간 캐시는 즉시 해제하세요.• None 모델을 여러 번 호출하는 구조라면, 인스턴스를 재사용하거나 컨텍스트를 관리하는 방식도 고려할 수 있습니다.궁극적으로 MLX는 Apple 생태계에서의 온디바이스 AI 구현을 훨씬 더 실용적으로 만들어주는 도구입니다.개발자에게는 성능과 프라이버시를 모두 챙길 수 있는 좋은 선택지가 될 수 있습니다.iOS 기반 앱에 AI 기능을 통합하고자 한다면 MLX는 반드시 눈여겨볼 만한 프레임워크입니다.
6/3/2025
logo
iOS 기기에서의 독립적 AI 실행: MLX 기반 온디바이스 모델
인터넷 없이도 똑똑한 iOS 앱, MLX로 만들기이번 포스팅에서는 iOS 개발 환경에서 주목받고 있는 온디바이스(On-device) AI 모델 실행 방식과,Apple Silicon에 최적화된 머신러닝 프레임워크 MLX에 대해 소개합니다.기술적 배경이 없는 비개발자부터 전문 개발자까지 모두 쉽게 이해할 수 있도록 구성했습니다.온디바이스 AI는 인터넷 연결 없이 스마트폰이나 태블릿과 같은 기기에서직접 인공지능(AI) 모델을 실행하여 데이터를 처리하는 기술입니다.데이터를 외부로 전송하지 않기 때문에 보안 측면에서 우수하며, 빠르고 안정적인 사용자 경험을 제공합니다.온디바이스 AI의 주요 장점데이터가 기기 외부로 전송되지 않기 때문에 개인정보 유출 위험이 낮습니다.네트워크 상태와 무관하게 빠른 응답을 제공할 수 있습니다.오프라인 환경에서도 AI 기능을 사용할 수 있어 활용 범위가 넓습니다.MLX는 Apple Silicon 기반의 기기에서 머신러닝 모델을 효과적으로 실행하기 위해 설계된PyTorch, TensorFlow처럼 배열 기반 연산을 지원하며,특히 Apple 하드웨어(iOS 및 macOS)에서 최적화된 성능을 제공합니다.iPhone, iPad, Mac 등에서 높은 성능과 전력 효율을 제공합니다.모델을 학습하거나 추론하는 작업을 모두 기기 내에서 수행할 수 있습니다.Swift와의 뛰어난 호환성Swift, SwiftUI와 쉽게 통합되어 앱 개발 생산성이 높아집니다.현재 MLX는 다음과 같은 다양한 LLM을 지원하고 있습니다:자세한 지원 모델은 Hugging Face MLX 문서에서 확인할 수 있습니다.SPM을 통해 MLX 라이브러리 추가온디바이스 AI는 성능과 효율 모두 중요합니다. MLX를 활용해 개발할 때 다음 사항을 고려해보세요.• None 모델이 너무 크면 앱 성능이 저하되거나 충돌이 발생할 수 있습니다.• None 가능한 한 경량화된 모델을 선택하고, 양자화(quantization) 또는 프루닝(pruning) 기법을 통해 최적화하세요.• None 경우에 따라 사전 학습 모델을 파인튜닝(fine-tuning) 해 실제 사용 시 연산량을 줄일 수 있습니다.• None 실시간 입력 처리(예: 음성 스트리밍)에서는 버퍼 관리가 핵심입니다.• None 입력/출력 데이터는 정제된 형태로 전달하고, 불필요한 중간 캐시는 즉시 해제하세요.• None 모델을 여러 번 호출하는 구조라면, 인스턴스를 재사용하거나 컨텍스트를 관리하는 방식도 고려할 수 있습니다.궁극적으로 MLX는 Apple 생태계에서의 온디바이스 AI 구현을 훨씬 더 실용적으로 만들어주는 도구입니다.개발자에게는 성능과 프라이버시를 모두 챙길 수 있는 좋은 선택지가 될 수 있습니다.iOS 기반 앱에 AI 기능을 통합하고자 한다면 MLX는 반드시 눈여겨볼 만한 프레임워크입니다.
2025.06.03
emoji
좋아요
emoji
별로에요
Copyright © 2025. Codenary All Rights Reserved.