
[현장으로 간 리서처] EP1. 커피값은 저희가 낼게요, 테스트 참여 해보실래요?
토스플레이스의 결제 단말기, 프론트를 소개합니다요즘 오프라인 매장에서 자주 보이는 "이쁜" 결제 단말기, 혹시 보신 적 있나요?토스플레이스에서 만든 단말기, 프론트예요. 프론트는 사장님들이 매장을 더욱 효율적으로 운영할 수 있도록 다양한 기능을 제공해요. 키오스크가 될수도 있고, 신메뉴 홍보도 할 수 있고, 토스 이벤트에 참여할 수도 있죠. 얼굴로 결제까지 할 수 있고요!할 수 있는 일이 정말 많지 않나요? 기능이 다양한 만큼, 리서치로 검증해야하는 부분도 많았는데요. 오늘은 그중 하나의 리서치 사례를 소개해드릴게요.카드사 할인 정보를 결제 중에 본다면 어떨까?매장에서 이런 광고를 보신 적 있으신가요? 특정 카드로 결제하면 할인을 받을 수 있다는 내용인데요.이런 정보를 결제하는 도중, 단말기 화면에서 자연스럽게 볼 수 있다면 어떨까요?결제 과정에서 할인 정보를 안내 받을 수 있다면, 더 매끄러운 결제 경험이 되지 않을까요?이 가설을 검증하기 위해 리서치를 시작했어요.실제 결제 단말기에서 카드 할인 정보를 이해할 수 있는지, 내 카드와 연결해 인지할 수 있는지를 확인하는 게 핵심이었죠.하지만 막상 리서치를 어떻게 설계할지 막막했어요. 오프라인 결제 상황을 어떻게 똑같이 구현해 테스트할 수 있을지 고민이 많았거든요. UT룸을 카페처럼 꾸며 상황을 최대한 구체적으로 연출해보는 것도 생각해봤지만 결국 신뢰도에 한계가 있을 것 같았어요.혼자 조용히 커피 마시며 결제할 때, 친구들과 수다 떨며 계산할 때, 직장 동료들과 점심시간에 급하게 결제할 때…고객의 주의력과 행동은 상황에 따라 크게 달라지는데요. UT룸 같이 인위적인 환경에서는 이런 맥락을 제대로 반영하기 어려우니까요.그래서 결심했죠. 진짜 오프라인 매장에서 테스트하자!진짜 매장으로 가다‘심플리시티’라는 카페에 찾아갔어요. 이곳은 토스플레이스가 직접 운영하는 공간으로, 만드는 다양한 오프라인 제품을 실험하기 위해 만들어진 매장이에요. 실제로 손님이 오가는 ‘진짜 매장’이기도 하죠. (참고로 커피 맛집으로도 소문났어요!)실제 고객이 자연스럽게 결제하는 상황에서 단말기 UX를 테스트하면, 인위적인 환경에서는 얻을 수 없는 생생한 반응을 관찰할 수 있다고 생각했어요.현장에서 손님을 리쿠르팅해 게릴라 테스트를 진행하기로 하고, 메뉴를 고르던 고객에게 조심스럽게 다가가 여쭤봤어요.저희가 준비한 시안은 3개였어요.• None 온라인 이커머스와 유사한 퍼널 구조각 시안을 손님들에게 보여드리며, 할인 정보를 얼마나 잘 인지하는지, 자신의 카드와 연결해 실제 결제 행동까지 이어지는지를 중점적으로 관찰했어요.혼자 온 손님부터 친구들과 함께 온 그룹, 점심시간에 급히 결제하는 직장인까지… 다양한 상황에서 고객의 시선 흐름, 주의력, 정보 인지 난이도를 세심하게 살폈죠.테스트는 이틀에 걸쳐 진행했어요. 첫째 날에는 각 시안에 대한 고객 반응을 관찰하며 가설을 정리했고,둘째 날에는 이를 바탕으로 디자인을 수정해 다시 테스트했어요.가설이 검증되는 순간을 보다시안 1은 할인 정보가 반복적으로 노출되며 정보 인지도가 높
6/4/2025

[현장으로 간 리서처] EP1. 커피값은 저희가 낼게요, 테스트 참여 해보실래요?
토스플레이스의 결제 단말기, 프론트를 소개합니다요즘 오프라인 매장에서 자주 보이는 "이쁜" 결제 단말기, 혹시 보신 적 있나요?토스플레이스에서 만든 단말기, 프론트예요. 프론트는 사장님들이 매장을 더욱 효율적으로 운영할 수 있도록 다양한 기능을 제공해요. 키오스크가 될수도 있고, 신메뉴 홍보도 할 수 있고, 토스 이벤트에 참여할 수도 있죠. 얼굴로 결제까지 할 수 있고요!할 수 있는 일이 정말 많지 않나요? 기능이 다양한 만큼, 리서치로 검증해야하는 부분도 많았는데요. 오늘은 그중 하나의 리서치 사례를 소개해드릴게요.카드사 할인 정보를 결제 중에 본다면 어떨까?매장에서 이런 광고를 보신 적 있으신가요? 특정 카드로 결제하면 할인을 받을 수 있다는 내용인데요.이런 정보를 결제하는 도중, 단말기 화면에서 자연스럽게 볼 수 있다면 어떨까요?결제 과정에서 할인 정보를 안내 받을 수 있다면, 더 매끄러운 결제 경험이 되지 않을까요?이 가설을 검증하기 위해 리서치를 시작했어요.실제 결제 단말기에서 카드 할인 정보를 이해할 수 있는지, 내 카드와 연결해 인지할 수 있는지를 확인하는 게 핵심이었죠.하지만 막상 리서치를 어떻게 설계할지 막막했어요. 오프라인 결제 상황을 어떻게 똑같이 구현해 테스트할 수 있을지 고민이 많았거든요. UT룸을 카페처럼 꾸며 상황을 최대한 구체적으로 연출해보는 것도 생각해봤지만 결국 신뢰도에 한계가 있을 것 같았어요.혼자 조용히 커피 마시며 결제할 때, 친구들과 수다 떨며 계산할 때, 직장 동료들과 점심시간에 급하게 결제할 때…고객의 주의력과 행동은 상황에 따라 크게 달라지는데요. UT룸 같이 인위적인 환경에서는 이런 맥락을 제대로 반영하기 어려우니까요.그래서 결심했죠. 진짜 오프라인 매장에서 테스트하자!진짜 매장으로 가다‘심플리시티’라는 카페에 찾아갔어요. 이곳은 토스플레이스가 직접 운영하는 공간으로, 만드는 다양한 오프라인 제품을 실험하기 위해 만들어진 매장이에요. 실제로 손님이 오가는 ‘진짜 매장’이기도 하죠. (참고로 커피 맛집으로도 소문났어요!)실제 고객이 자연스럽게 결제하는 상황에서 단말기 UX를 테스트하면, 인위적인 환경에서는 얻을 수 없는 생생한 반응을 관찰할 수 있다고 생각했어요.현장에서 손님을 리쿠르팅해 게릴라 테스트를 진행하기로 하고, 메뉴를 고르던 고객에게 조심스럽게 다가가 여쭤봤어요.저희가 준비한 시안은 3개였어요.• None 온라인 이커머스와 유사한 퍼널 구조각 시안을 손님들에게 보여드리며, 할인 정보를 얼마나 잘 인지하는지, 자신의 카드와 연결해 실제 결제 행동까지 이어지는지를 중점적으로 관찰했어요.혼자 온 손님부터 친구들과 함께 온 그룹, 점심시간에 급히 결제하는 직장인까지… 다양한 상황에서 고객의 시선 흐름, 주의력, 정보 인지 난이도를 세심하게 살폈죠.테스트는 이틀에 걸쳐 진행했어요. 첫째 날에는 각 시안에 대한 고객 반응을 관찰하며 가설을 정리했고,둘째 날에는 이를 바탕으로 디자인을 수정해 다시 테스트했어요.가설이 검증되는 순간을 보다시안 1은 할인 정보가 반복적으로 노출되며 정보 인지도가 높
2025.06.04

좋아요

별로에요

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

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

좋아요

별로에요

생산성을 높이는 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

생산성을 높이는 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

좋아요

별로에요

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

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

좋아요

별로에요

어서와요 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

어서와요 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

좋아요

별로에요

코드 품질 개선 기법 14편: 책임을 부여하는 오직 하나의 책임
안녕하세요. 커뮤니케이션 앱 LINE의 모바일 클라이언트를 개발하고 있는 Ishikawa입니다.저희 회사는 높은 개발 생산성을 유지하기 위해 코드 품질 및 개발 문화 개선에 힘쓰고 있습니다. 이를 위해 다양한 노력을 하고 있는데요. 그중 하나가 Review Committee 활동입니다.Review Committee에서는 머지된 코드를 다시 리뷰해 리뷰어와 작성자에게 피드백을 주고, 리뷰하면서 얻은 지식과 인사이트를 Weekly Report라는 이름으로 매주 공유하고 있습니다. 이 Weekly Report 중 일반적으로 널리 적용할 수 있는 주제를 골라 블로그에 코드 품질 개선 기법 시리즈를 연재하고 있습니다.이번에 블로그로 공유할 Weekly Report의 제목은 '책임을 부여하는 오직 하나의 책임'입니다.책임을 부여하는 오직 하나의 책임불꽃놀이, 로켓, 프로덕트 중 하나를 론치(launch)하는 '론치 버튼'을 구현하고 싶다고 가정해 봅시다. 이 론치 버튼은 '눌렀을 때 무엇을 실행할 것인가'를 동적으로 변경해야 합니다.다음 는 론치할 로직 를 생성자 인수로 받습니다. 그리고 버튼을 눌렀을 때 적절한 가 실행되도록 에서 이벤트 리스너를 등록합니다.이 바인더 클래스는 충분히 단순하지만, '버튼이 눌렸을 때 로직을 바인딩하는 책임'과 '어떤 가 유효한지 결정하는 책임'의 두 가지 책임이 있습니다. 이를 개선하기 위해 가 하나의 와 바인딩되도록 변경하고 를 선택하는 기능은 라는 별도 클래스로 분리 구현하기로 결정했습니다.아래 코드에서는 인스턴스가 생성될 때 각 별로 인스턴스가 생성됩니다. 각 에서 이벤트 리스너를 등록하기 때문에 버튼에 등록되는 리스너는 총 세 개입니다. 세 개의 리스너 중 어떤 리스너를 실행할지 제어하기 위해 라는 속성을 추가했습니다.이렇게 해서 버튼에 대한 로직 구현과 의 책임을 분리할 수 있었습니다. 하지만 오히려 새로운 문제도 발생했습니다. 어떤 문제일까요?새로운 문제점 중 하나는 '사양의 제약 조건을 보장하는 책임이 분산된다'는 점입니다. 변경 전 코드에서는 버튼을 눌렀을 때 셋 중 오직 한 가지만 실행된다는 제약 조건이 있었습니다. 새로운 코드에서도 , , 의 조합을 통해 이를 구현했지만, 이것이 올바른지 확인하려면 모든 코드를 읽어야 합니다. 이 문제는 '어떤 가 선택됐는지'를 나타내는 속성을 에 추가해 해결할 수 있지만, 이렇게 하면 이번에는 추가한 속성과 사이에 상태 중복이 발생합니다.또한 세부 사항 은닉과 관련된 문제도 있습니다. 분리된 코드에서 는 를 선택하는 것만 담당하도록 설계하고자 했습니다. 그러나 인스턴스를 생성하려면 에 직접 의존해야 합니다. 이를 개선하기 위해 를 로부터 숨기도록 아래와 같이 의 인스턴스를 직접 보유하는 방법도 생각해 볼 수 있습니다.하지만 이렇게 되면 호출자가 모든 의존성( , , , )을 해결해야 하기 때문에 결과적으로 호출자가 비대해지거나(소위 God class) 전체 그림을 파악하기 위해 수많은 클래스를 읽어야 하는 경우가 발생하기도(소위 Ravioli code) 합니다.책임을 고려하는 책임의 초기 구현은 사양의 제약 조건을 한 곳에서 구현했다는 장점이 있기 때문에 종합적으로 생각해 봤을 때 더 이상 클래스를 분할할 필요가 없었을 수도 있습니다. 클래스를 분할할 때는 '개별 클래스의 책임 범위나 응집도에 지나치게 집중한 나머지 호출자의 책임이 비대해지거나 클래스 및 모듈 간의 의존성과 결합도가 악화되는 것을 피해야 한다'는 것을 고려해야 합니다.
6/4/2025

코드 품질 개선 기법 14편: 책임을 부여하는 오직 하나의 책임
안녕하세요. 커뮤니케이션 앱 LINE의 모바일 클라이언트를 개발하고 있는 Ishikawa입니다.저희 회사는 높은 개발 생산성을 유지하기 위해 코드 품질 및 개발 문화 개선에 힘쓰고 있습니다. 이를 위해 다양한 노력을 하고 있는데요. 그중 하나가 Review Committee 활동입니다.Review Committee에서는 머지된 코드를 다시 리뷰해 리뷰어와 작성자에게 피드백을 주고, 리뷰하면서 얻은 지식과 인사이트를 Weekly Report라는 이름으로 매주 공유하고 있습니다. 이 Weekly Report 중 일반적으로 널리 적용할 수 있는 주제를 골라 블로그에 코드 품질 개선 기법 시리즈를 연재하고 있습니다.이번에 블로그로 공유할 Weekly Report의 제목은 '책임을 부여하는 오직 하나의 책임'입니다.책임을 부여하는 오직 하나의 책임불꽃놀이, 로켓, 프로덕트 중 하나를 론치(launch)하는 '론치 버튼'을 구현하고 싶다고 가정해 봅시다. 이 론치 버튼은 '눌렀을 때 무엇을 실행할 것인가'를 동적으로 변경해야 합니다.다음 는 론치할 로직 를 생성자 인수로 받습니다. 그리고 버튼을 눌렀을 때 적절한 가 실행되도록 에서 이벤트 리스너를 등록합니다.이 바인더 클래스는 충분히 단순하지만, '버튼이 눌렸을 때 로직을 바인딩하는 책임'과 '어떤 가 유효한지 결정하는 책임'의 두 가지 책임이 있습니다. 이를 개선하기 위해 가 하나의 와 바인딩되도록 변경하고 를 선택하는 기능은 라는 별도 클래스로 분리 구현하기로 결정했습니다.아래 코드에서는 인스턴스가 생성될 때 각 별로 인스턴스가 생성됩니다. 각 에서 이벤트 리스너를 등록하기 때문에 버튼에 등록되는 리스너는 총 세 개입니다. 세 개의 리스너 중 어떤 리스너를 실행할지 제어하기 위해 라는 속성을 추가했습니다.이렇게 해서 버튼에 대한 로직 구현과 의 책임을 분리할 수 있었습니다. 하지만 오히려 새로운 문제도 발생했습니다. 어떤 문제일까요?새로운 문제점 중 하나는 '사양의 제약 조건을 보장하는 책임이 분산된다'는 점입니다. 변경 전 코드에서는 버튼을 눌렀을 때 셋 중 오직 한 가지만 실행된다는 제약 조건이 있었습니다. 새로운 코드에서도 , , 의 조합을 통해 이를 구현했지만, 이것이 올바른지 확인하려면 모든 코드를 읽어야 합니다. 이 문제는 '어떤 가 선택됐는지'를 나타내는 속성을 에 추가해 해결할 수 있지만, 이렇게 하면 이번에는 추가한 속성과 사이에 상태 중복이 발생합니다.또한 세부 사항 은닉과 관련된 문제도 있습니다. 분리된 코드에서 는 를 선택하는 것만 담당하도록 설계하고자 했습니다. 그러나 인스턴스를 생성하려면 에 직접 의존해야 합니다. 이를 개선하기 위해 를 로부터 숨기도록 아래와 같이 의 인스턴스를 직접 보유하는 방법도 생각해 볼 수 있습니다.하지만 이렇게 되면 호출자가 모든 의존성( , , , )을 해결해야 하기 때문에 결과적으로 호출자가 비대해지거나(소위 God class) 전체 그림을 파악하기 위해 수많은 클래스를 읽어야 하는 경우가 발생하기도(소위 Ravioli code) 합니다.책임을 고려하는 책임의 초기 구현은 사양의 제약 조건을 한 곳에서 구현했다는 장점이 있기 때문에 종합적으로 생각해 봤을 때 더 이상 클래스를 분할할 필요가 없었을 수도 있습니다. 클래스를 분할할 때는 '개별 클래스의 책임 범위나 응집도에 지나치게 집중한 나머지 호출자의 책임이 비대해지거나 클래스 및 모듈 간의 의존성과 결합도가 악화되는 것을 피해야 한다'는 것을 고려해야 합니다.
2025.06.04

좋아요

별로에요

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

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

좋아요

별로에요

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

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

좋아요

별로에요

수이 기반 탈중앙화 거래소 세투스 해킹으로... 이해하는 web3 용어
Cetus의 해킹으로 한번 떠들썩 했는데요이번 해커 (공격자는) overflow 체크 취약점을 이용했습니다.전반적으로 관련된 용어를 설명하면서 그 내용도 같이 알아보겠습니다.해커들은 세투스의 자동화된 시장 조성자(AMM) 프로토콜에서 유동성 매개변수의 가장 중요한 비트(MSB) 검증에 존재하는 취약점을 악용했습니다. 이로 인해 단 한 번의 키 입력으로 대규모 유동성 포지션을 형성하고, 이를 통해 수백만 달러 상당의 자산을 탈취할 수 있었습니다. 총 2억 2,300만 달러 상당의 자산이 탈취되었으며, 이 중 약 1억 6,300만 달러는 수이 네트워크의 검증자들에 의해 동결되었습니다. 일부 커뮤니티 구성원들은 검증자들의 자산 동결 조치가 탈중앙화 원칙을 훼손한다고 비판했습니다. 이는 블록체인의 검열 저항성과 관련된 논쟁을 촉발시켰습니다.세투스는 Sui와 Aptos 블록체인 기반의 탈중앙화 거래소 (DEX) 입니다.세투스는 CLMM( Concentrated Liquidity Market Maker : 집중 유동성 시장 조성자) 모델을 도입했습니다.기존의 AMM ( Automated Market Maker) 모델은 일반적으로 Constant Product Formula ( x * y = k ) 를 사용합니다.AMM 최초에 공급자가 예치하는 토큰을 기준으로 x * y = k 를 결정합니다.A라는 유동성 공급자가 이더 2 개와 USDC 1,500 개를 예치하고 유동성 pool을 생성을 하면이더리움과 USDC는 2 * 1500 = 3000 의 공식이 성립하게 되고, 3000 이라는 숫자는 바꾸지 않게 됩니다.이때 1 이더의 가치는 750 USDC로 볼 수 있습니다. 최초에는 가격차이가 날 수 있지만,거래 차익을 얻기 위한 노력으로 시장 평균으로 수렴하게 됩니다.단 B라는 사람이 1개의 이더를 거래하기 위해서 USDC 750개를 넣었다고 하면Constant Product Formula ( x * y = k ) 에 의해서 0.6666..7 개의 이더만 확보하게 됩니다. (거래순간 이더의 가치가 올라가 버립니다)초기 가치와 비교해서 0.33333개 를 확보하지 못하게 되는데이때 이 0.33333 개에 해당하는 가치에 대해서 슬리피지가 발생했다 합니다.이 AMM 모델은 0 부터 무한대까지 모든 가격범위에 걸쳐 균등하게 분포 되지만, 유동성 pool이 작을때는위에서 처럼 슬리피지가 발생합니다. 하지만 토큰의 가치가 급변하기 보다는 특정 가격 범위에서 주로 거래가 됩니다.하지만 전체 가격범위에 토큰의 유동성이 공급되다 보니, 실제 거래가 자주 발생하는 범위에서는 유동성이 부족해 지게 되는 현상이 발생합니다.CLMM은 특정 범위를 정해 놓고, 해당 범위에 대해서만 유동성을 공급하게 됨으로낮은 슬리피지와 거래가 활성화 되고, 거래 수수료를 더 많이 받을 수 있는 장점이 있습니다.세투스는 CLMM 모델을 통해 유동성 공급자들이 특정 가격 범위 내에서 자산을 제공함으로써 자본 효율성을 극대화하고, 거래자들에게는 낮은 슬리피지와 빠른 거래를 제공합니다.CLMM에서는 유동성 공급자가 특정 가격 범위에 자산을 공급합니다.그 범위 안에서는 항상 두 가지 자산이 일정한 비율로 교환 가능해야 하죠.이걸 가능하게 하려면 유동성이 일정해야 하고, 이 유동성은 가격 범위 내에서 루트 가격 기반의 계산으로 정해집니다.유동성을 구하는 공식은즉 루트 가격 범위의 차이가 작을수록, 동일한 토큰 양으로 더 큰 liquidity 값이 계산됩니다.이해를 돕기위해서 가상의 예를 든다면 1개의 이더를 1000원 ~ 10000원 사이에 공급하기로 했다고 하면 1/ (10-1) 로 유동성이 계산되는 방식입니다.실제로는 금액이 아니라 Tick이라는 단위로 계산이 되어집니다.공격자(해커)는 매우 좁은 가격 범위(예: [300000, 300200])를 설정하여 유동성을 추가했습니다.이러한 좁은 범위는 sqrt_price_diff 값을 작게 만들어, 이후 계산의 결과가 오버플로우를 발생은 하지만 임계값을 넘지 않도록 만들었습니다.Cetus의 스마트 계약에서는 checked_shlw 함수를 사용하여 오버플로우를 검사합니다.그러나 이 함수의 구현에서 사용된 마스크 값이 잘못되어, 실제로 오버플로우가 발생해도 이를 감지하지 못했습니다.공격자는 이 취약점을 이용하여, 오버플로우를 유도하면서도 검사에는 통과하는 값을 입력할 수 있었습니다.이런 방식으로 실제 1Token 만 공급하였지만, 오버플로우로 인해서 대용량의 유동성을 공급 받습니다.큰 유동성을 제공한 것으로 인정받아, LP토큰을 받게 됩니다. LP토큰은 유동성 Pool 내의 지분을 의미합니다.이렇게 1 토큰으로 LP토큰을 받은 뒤에 다시 자산을 회수하는 방식으로 2억 2,300만 달러의 자산을 탈취했습니다.자산 동결이 가능한 이유Sui 블록체인은 Move 언어를 기반이며 자산의 이동이 검증자의 승인을 필요로 하는 구조입니다.즉, 자산의 소유자가 트랜잭션을 생성하더라도, 검증자들이 이를 승인하지 않으면 해당 자산은 이동할 수 없습니다.검증자들이 협력하여 해커의 주소에서 발생하는 트랜잭션을 처리하지 않음으로써, 해당 자산을 사실상 동결할 수 있습니다.이번 사태가 발생한 후에 보안 전문가들과 Sui커뮤니티에서 해커 주소 식별 → 검증자 설정 변경 → 협력적 실행 을 통해서 자산을 동결했습니다.나머지 약 6,000만 달러는 해커가 Sui 네트워크를 벗어나 이더리움 체인으로 브리지로 이전하여 동결이 어려웠습니다.검증자들의 협력은 사용자 자산 보호라는 측면에서 긍정적으로 평가되지만,동시에 블록체인의 탈중앙화 원칙에 대한 우려가 있습니다.일부 커뮤니티 구성원들은 검증자들이 특정 주소의 트랜잭션을 임의로 차단할 수 있는 권한을 가지는 것이검열의 위험성을 내포한다고 말합니다Sui 재단은 이러한 우려를 인식하고, 동결된 자산의 반환 여부를 커뮤니티의 거버넌스 투표를 통해 결정하도록 제안했습니다.
6/2/2025

수이 기반 탈중앙화 거래소 세투스 해킹으로... 이해하는 web3 용어
Cetus의 해킹으로 한번 떠들썩 했는데요이번 해커 (공격자는) overflow 체크 취약점을 이용했습니다.전반적으로 관련된 용어를 설명하면서 그 내용도 같이 알아보겠습니다.해커들은 세투스의 자동화된 시장 조성자(AMM) 프로토콜에서 유동성 매개변수의 가장 중요한 비트(MSB) 검증에 존재하는 취약점을 악용했습니다. 이로 인해 단 한 번의 키 입력으로 대규모 유동성 포지션을 형성하고, 이를 통해 수백만 달러 상당의 자산을 탈취할 수 있었습니다. 총 2억 2,300만 달러 상당의 자산이 탈취되었으며, 이 중 약 1억 6,300만 달러는 수이 네트워크의 검증자들에 의해 동결되었습니다. 일부 커뮤니티 구성원들은 검증자들의 자산 동결 조치가 탈중앙화 원칙을 훼손한다고 비판했습니다. 이는 블록체인의 검열 저항성과 관련된 논쟁을 촉발시켰습니다.세투스는 Sui와 Aptos 블록체인 기반의 탈중앙화 거래소 (DEX) 입니다.세투스는 CLMM( Concentrated Liquidity Market Maker : 집중 유동성 시장 조성자) 모델을 도입했습니다.기존의 AMM ( Automated Market Maker) 모델은 일반적으로 Constant Product Formula ( x * y = k ) 를 사용합니다.AMM 최초에 공급자가 예치하는 토큰을 기준으로 x * y = k 를 결정합니다.A라는 유동성 공급자가 이더 2 개와 USDC 1,500 개를 예치하고 유동성 pool을 생성을 하면이더리움과 USDC는 2 * 1500 = 3000 의 공식이 성립하게 되고, 3000 이라는 숫자는 바꾸지 않게 됩니다.이때 1 이더의 가치는 750 USDC로 볼 수 있습니다. 최초에는 가격차이가 날 수 있지만,거래 차익을 얻기 위한 노력으로 시장 평균으로 수렴하게 됩니다.단 B라는 사람이 1개의 이더를 거래하기 위해서 USDC 750개를 넣었다고 하면Constant Product Formula ( x * y = k ) 에 의해서 0.6666..7 개의 이더만 확보하게 됩니다. (거래순간 이더의 가치가 올라가 버립니다)초기 가치와 비교해서 0.33333개 를 확보하지 못하게 되는데이때 이 0.33333 개에 해당하는 가치에 대해서 슬리피지가 발생했다 합니다.이 AMM 모델은 0 부터 무한대까지 모든 가격범위에 걸쳐 균등하게 분포 되지만, 유동성 pool이 작을때는위에서 처럼 슬리피지가 발생합니다. 하지만 토큰의 가치가 급변하기 보다는 특정 가격 범위에서 주로 거래가 됩니다.하지만 전체 가격범위에 토큰의 유동성이 공급되다 보니, 실제 거래가 자주 발생하는 범위에서는 유동성이 부족해 지게 되는 현상이 발생합니다.CLMM은 특정 범위를 정해 놓고, 해당 범위에 대해서만 유동성을 공급하게 됨으로낮은 슬리피지와 거래가 활성화 되고, 거래 수수료를 더 많이 받을 수 있는 장점이 있습니다.세투스는 CLMM 모델을 통해 유동성 공급자들이 특정 가격 범위 내에서 자산을 제공함으로써 자본 효율성을 극대화하고, 거래자들에게는 낮은 슬리피지와 빠른 거래를 제공합니다.CLMM에서는 유동성 공급자가 특정 가격 범위에 자산을 공급합니다.그 범위 안에서는 항상 두 가지 자산이 일정한 비율로 교환 가능해야 하죠.이걸 가능하게 하려면 유동성이 일정해야 하고, 이 유동성은 가격 범위 내에서 루트 가격 기반의 계산으로 정해집니다.유동성을 구하는 공식은즉 루트 가격 범위의 차이가 작을수록, 동일한 토큰 양으로 더 큰 liquidity 값이 계산됩니다.이해를 돕기위해서 가상의 예를 든다면 1개의 이더를 1000원 ~ 10000원 사이에 공급하기로 했다고 하면 1/ (10-1) 로 유동성이 계산되는 방식입니다.실제로는 금액이 아니라 Tick이라는 단위로 계산이 되어집니다.공격자(해커)는 매우 좁은 가격 범위(예: [300000, 300200])를 설정하여 유동성을 추가했습니다.이러한 좁은 범위는 sqrt_price_diff 값을 작게 만들어, 이후 계산의 결과가 오버플로우를 발생은 하지만 임계값을 넘지 않도록 만들었습니다.Cetus의 스마트 계약에서는 checked_shlw 함수를 사용하여 오버플로우를 검사합니다.그러나 이 함수의 구현에서 사용된 마스크 값이 잘못되어, 실제로 오버플로우가 발생해도 이를 감지하지 못했습니다.공격자는 이 취약점을 이용하여, 오버플로우를 유도하면서도 검사에는 통과하는 값을 입력할 수 있었습니다.이런 방식으로 실제 1Token 만 공급하였지만, 오버플로우로 인해서 대용량의 유동성을 공급 받습니다.큰 유동성을 제공한 것으로 인정받아, LP토큰을 받게 됩니다. LP토큰은 유동성 Pool 내의 지분을 의미합니다.이렇게 1 토큰으로 LP토큰을 받은 뒤에 다시 자산을 회수하는 방식으로 2억 2,300만 달러의 자산을 탈취했습니다.자산 동결이 가능한 이유Sui 블록체인은 Move 언어를 기반이며 자산의 이동이 검증자의 승인을 필요로 하는 구조입니다.즉, 자산의 소유자가 트랜잭션을 생성하더라도, 검증자들이 이를 승인하지 않으면 해당 자산은 이동할 수 없습니다.검증자들이 협력하여 해커의 주소에서 발생하는 트랜잭션을 처리하지 않음으로써, 해당 자산을 사실상 동결할 수 있습니다.이번 사태가 발생한 후에 보안 전문가들과 Sui커뮤니티에서 해커 주소 식별 → 검증자 설정 변경 → 협력적 실행 을 통해서 자산을 동결했습니다.나머지 약 6,000만 달러는 해커가 Sui 네트워크를 벗어나 이더리움 체인으로 브리지로 이전하여 동결이 어려웠습니다.검증자들의 협력은 사용자 자산 보호라는 측면에서 긍정적으로 평가되지만,동시에 블록체인의 탈중앙화 원칙에 대한 우려가 있습니다.일부 커뮤니티 구성원들은 검증자들이 특정 주소의 트랜잭션을 임의로 차단할 수 있는 권한을 가지는 것이검열의 위험성을 내포한다고 말합니다Sui 재단은 이러한 우려를 인식하고, 동결된 자산의 반환 여부를 커뮤니티의 거버넌스 투표를 통해 결정하도록 제안했습니다.
2025.06.02

좋아요

별로에요

당신이 보는 첫 화면은 어떻게 정해질까? 무신사 홈 배너 개인화 추천 이야기
안녕하세요, 무신사 타겟팅ML팀의 방효석, 조준형입니다. 저희 팀은 다양한 AI/ML 기술을 활용해 무신사의 모든 상품과 비즈니스 접점에서 개인화된 고객 경험을 제공하는 것을 목표로 하고 있습니다. 지난 1분기 동안 디스플레이 지면의 CTR(Click-trhough Rate; 클릭률)을 더욱 개선하기 위해, 무신사 및 6개 전문관 스토어 홈 지면의 개인화된 배너 노출 로직을 개발하는 프로젝트를 진행했습니다. 이번 글에서는 프로젝트를 수행하며 마주했던 여러 가지 문제들을 어떻게 해결했는지, 그리고 그 과정에서 얻은 인사이트들을 함께 공유하고자 합니다.고객 맞춤 빅배너로 더 나은 경험 만들기무신사를 이용하는 고객들의 취향과 니즈는 과거보다 훨씬 다양해졌으며, 노출 가능한 콘텐츠 수도 꾸준히 늘고 있습니다. 하지만 이러한 복잡한 상황과 세분화된 니즈를 충분히 반영하지 못한 채 일괄적으로 노출되는 배너는 고객 경험을 저해할 수 있다는 문제 제기가 있었습니다. 이에 분명한 개선의 여지가 있다고 판단했고, 디스플레이 최적화 라는 이름으로 각 스토어 홈 상단 빅배너의 노출 방식을 개인화하여 CTR을 극대화하는 프로젝트를 진행하게 되었습니다.무신사 서비스 첫인상, 스토어 홈 배너 소개<그림 1. 무신사 홈 배너: 앱(좌측), 웹(우측)>스토어 홈 빅배너는 무신사 앱과 웹을 열었을 때 가장 먼저 마주치는 화면 맨 위 배너입니다. 웹사이트의 간판 같은 존재라 무신사가 전하고 싶은 브랜드 감성과 콘텐츠를 첫눈에 보여 주죠. 슬라이드 형식으로 총 35장이 돌아가며 노출되고, 앱에서는 한 번에 1장, 웹에서는 3장이 함께 보입니다.무신사에 접속한 고객 가운데 약 97 %는 메인 화면에 도착하자마자 홈 배너를 가장 먼저 만납니다. 이 배너는 무신사 스토어뿐 아니라 뷰티·플레이어 등 6개 전문관 홈에도 같은 자리에 노출되며, 무신사 서비스의 첫인상을 책임지고 있습니다.홈 배너 최적화가 필요한 이유무신사에는 여러 전시 지면이 있지만, 홈 배너를 먼저 최적화하기로 한 이유는 분명합니다. 홈 배너는 고객이 무신사에 접속해 가장 먼저 마주치는 서비스의 얼굴 로, 하루 수십만 명에게 노출되기 때문입니다. 또한 메인 화면에 위치한 여러 디스플레이 영역 중에서도 홈 배너는 특히 고객의 관심도가 높습니다. 실제로 전체 클릭 수의 약 35%, 클릭이 발생한 세션의 약 37%가 홈 배너에서 발생하고 있습니다. 이러한 지표를 근거로, 전환 효과가 가장 큰 홈 배너부터 개선을 시작하기로 결정했습니다.홈 배너 추천, 왜 더 똑똑한 방법이 필요했을까?기존에도 홈 배너의 노출 순서는 모델 기반으로 최적화되고 있었지만, 완전한 수준의 초개인화 추천 시스템은 아니었습니다. 과거에는 MAB(Multi-Armed Bandit) 알고리즘을 활용해 배너 추천을 진행해 왔습니다.MAB 알고리즘은 제한된 시도 안에서 여러 선택지(배너) 중 가장 높은 보상(클릭률)을 얻기 위해, 탐험(Exploration) 과 활용(Exploitation) 사이의 균형을 조절하는 전략입니다. 여기서 탐험 은 새로운 배너의 가능성을 시험하는 과정이고, 활용 은 성과가 입증된 배너에 집중하는 것을 의미합니다. MAB는 이 두 과정을 조율하며, 시간이 지남에 따라 성과가 좋은 배너를 더 자주 노출하는 방식으로 학습합니다.하지만 이 방식에는 세 가지 뚜렷한 한계가 있었습니다.단일 지표 의존 클릭률 하나만 바라보니 고객 취향, 선호, 배너 자체의 특성 같은 다층적 요인이 반영되지 않았습니다.연관성 미반영 MAB가 각 배너를 서로 독립적으로 간주해, 사용자가 예전에 관심을 보인 배너와의 유사성을 고려하지 못했습니다.콜드 스타트(Cold Start) 문제 새 배너가 투입되면 클릭 데이터가 쌓일 때까지 성능이 저조했습니다. 결국 고객 경험이 크게 좌우되는 홈 배너 영역에서 클릭률만으로는 섬세한 니즈를 채우기 어렵고, 빠르게 변하는 환경에도 기민하게 대응하기 힘들었습니다.이러한 문제들을 해결하고 사용자에게 더 정교한 개인화 추천을 제공하기 위해서는 기존과 다른 새로운 접근 방식이 필요했습니다. 다음 장에서는 이러한 고민을 바탕으로 새롭게 설계한 추천 시스템의 전체 파이프라인 구조에 대해 자세히 소개드리겠습니다.스토어 홈 빅배너 추천 시스템 구조<그림 2. 무신사 홈 배너 추천 시스템 아키텍쳐>무신사 홈 배너 추천 시스템은 여러 단계로 이루어진 정교한 파이프라인을 통해 각 사용자에게 가장 알맞은 배너를 실시간으로 보여 줍니다. 그림 2는 이러한 배너 추천 시스템의 전체적인 구조를 설명하고 있습니다. 각 주요 단계 별 구체적인 내용은 아래와 같습니다.1. 추천할 배너를 더 잘 이해하기 GIGO(Garbage In, Garbage Out) 라는 말처럼, 좋은 모델을 학습하기 위해서는 양질의 표현(Representation)을 추출하는 것이 매우 중요합니다. 기존에도 배너 타이틀, 연관 카테고리, 브랜드, 라벨 등 다양한 메타 데이터가 DB에 저장되어 있으나, 이 정보들은 운영 목적에 따라 적재된 데이터이기 때문에, 추천 모델 관점에서 일관된 기준을 유지하기 어렵다는 한계가 있었습니다.이러한 문제를 극복하기 위해, 저희는 사용자 행동 로그를 활용한 표현(Representation) 학습 방식으로 접근했습니다. 일정 기간 동안 수집된 유저의 배너 및 상품 클릭 이력을 기반으로 이종 그래프(Heterogeneous Graph)를 구성하고, 이를 GraphSAGE 알고리즘에 적용했습니다. 이 과정을 통해 사용자 탐색 패턴을 반영한 배너 및 상품 임베딩(Embedding)을 생성할 수 있었으며, 이렇게 추출된 임베딩은 모든 노출 가능 배너 후보군에 대해 계산되어 이후 모델 학습의 입력 피처(Input Feature)로 활용됩니다.2. 클릭 확률을 계산하기 위한 모델 학습하기배너 추천 시스템의 핵심은 각 사용자에게 적합한 배너를 찾아주는 것 입니다. 저희는 이 문제를 많은 추천 시스템에서 채택하는 방식처럼 CTR(Click-Through Rate) 예측 문제로 정의했고, 이를 해결하기 위해 DeepFM과 Two-Tower 모델을 활용했습니다.앞선 단계에서 HGNN을 통해 학습된 고품
6/1/2025

당신이 보는 첫 화면은 어떻게 정해질까? 무신사 홈 배너 개인화 추천 이야기
안녕하세요, 무신사 타겟팅ML팀의 방효석, 조준형입니다. 저희 팀은 다양한 AI/ML 기술을 활용해 무신사의 모든 상품과 비즈니스 접점에서 개인화된 고객 경험을 제공하는 것을 목표로 하고 있습니다. 지난 1분기 동안 디스플레이 지면의 CTR(Click-trhough Rate; 클릭률)을 더욱 개선하기 위해, 무신사 및 6개 전문관 스토어 홈 지면의 개인화된 배너 노출 로직을 개발하는 프로젝트를 진행했습니다. 이번 글에서는 프로젝트를 수행하며 마주했던 여러 가지 문제들을 어떻게 해결했는지, 그리고 그 과정에서 얻은 인사이트들을 함께 공유하고자 합니다.고객 맞춤 빅배너로 더 나은 경험 만들기무신사를 이용하는 고객들의 취향과 니즈는 과거보다 훨씬 다양해졌으며, 노출 가능한 콘텐츠 수도 꾸준히 늘고 있습니다. 하지만 이러한 복잡한 상황과 세분화된 니즈를 충분히 반영하지 못한 채 일괄적으로 노출되는 배너는 고객 경험을 저해할 수 있다는 문제 제기가 있었습니다. 이에 분명한 개선의 여지가 있다고 판단했고, 디스플레이 최적화 라는 이름으로 각 스토어 홈 상단 빅배너의 노출 방식을 개인화하여 CTR을 극대화하는 프로젝트를 진행하게 되었습니다.무신사 서비스 첫인상, 스토어 홈 배너 소개<그림 1. 무신사 홈 배너: 앱(좌측), 웹(우측)>스토어 홈 빅배너는 무신사 앱과 웹을 열었을 때 가장 먼저 마주치는 화면 맨 위 배너입니다. 웹사이트의 간판 같은 존재라 무신사가 전하고 싶은 브랜드 감성과 콘텐츠를 첫눈에 보여 주죠. 슬라이드 형식으로 총 35장이 돌아가며 노출되고, 앱에서는 한 번에 1장, 웹에서는 3장이 함께 보입니다.무신사에 접속한 고객 가운데 약 97 %는 메인 화면에 도착하자마자 홈 배너를 가장 먼저 만납니다. 이 배너는 무신사 스토어뿐 아니라 뷰티·플레이어 등 6개 전문관 홈에도 같은 자리에 노출되며, 무신사 서비스의 첫인상을 책임지고 있습니다.홈 배너 최적화가 필요한 이유무신사에는 여러 전시 지면이 있지만, 홈 배너를 먼저 최적화하기로 한 이유는 분명합니다. 홈 배너는 고객이 무신사에 접속해 가장 먼저 마주치는 서비스의 얼굴 로, 하루 수십만 명에게 노출되기 때문입니다. 또한 메인 화면에 위치한 여러 디스플레이 영역 중에서도 홈 배너는 특히 고객의 관심도가 높습니다. 실제로 전체 클릭 수의 약 35%, 클릭이 발생한 세션의 약 37%가 홈 배너에서 발생하고 있습니다. 이러한 지표를 근거로, 전환 효과가 가장 큰 홈 배너부터 개선을 시작하기로 결정했습니다.홈 배너 추천, 왜 더 똑똑한 방법이 필요했을까?기존에도 홈 배너의 노출 순서는 모델 기반으로 최적화되고 있었지만, 완전한 수준의 초개인화 추천 시스템은 아니었습니다. 과거에는 MAB(Multi-Armed Bandit) 알고리즘을 활용해 배너 추천을 진행해 왔습니다.MAB 알고리즘은 제한된 시도 안에서 여러 선택지(배너) 중 가장 높은 보상(클릭률)을 얻기 위해, 탐험(Exploration) 과 활용(Exploitation) 사이의 균형을 조절하는 전략입니다. 여기서 탐험 은 새로운 배너의 가능성을 시험하는 과정이고, 활용 은 성과가 입증된 배너에 집중하는 것을 의미합니다. MAB는 이 두 과정을 조율하며, 시간이 지남에 따라 성과가 좋은 배너를 더 자주 노출하는 방식으로 학습합니다.하지만 이 방식에는 세 가지 뚜렷한 한계가 있었습니다.단일 지표 의존 클릭률 하나만 바라보니 고객 취향, 선호, 배너 자체의 특성 같은 다층적 요인이 반영되지 않았습니다.연관성 미반영 MAB가 각 배너를 서로 독립적으로 간주해, 사용자가 예전에 관심을 보인 배너와의 유사성을 고려하지 못했습니다.콜드 스타트(Cold Start) 문제 새 배너가 투입되면 클릭 데이터가 쌓일 때까지 성능이 저조했습니다. 결국 고객 경험이 크게 좌우되는 홈 배너 영역에서 클릭률만으로는 섬세한 니즈를 채우기 어렵고, 빠르게 변하는 환경에도 기민하게 대응하기 힘들었습니다.이러한 문제들을 해결하고 사용자에게 더 정교한 개인화 추천을 제공하기 위해서는 기존과 다른 새로운 접근 방식이 필요했습니다. 다음 장에서는 이러한 고민을 바탕으로 새롭게 설계한 추천 시스템의 전체 파이프라인 구조에 대해 자세히 소개드리겠습니다.스토어 홈 빅배너 추천 시스템 구조<그림 2. 무신사 홈 배너 추천 시스템 아키텍쳐>무신사 홈 배너 추천 시스템은 여러 단계로 이루어진 정교한 파이프라인을 통해 각 사용자에게 가장 알맞은 배너를 실시간으로 보여 줍니다. 그림 2는 이러한 배너 추천 시스템의 전체적인 구조를 설명하고 있습니다. 각 주요 단계 별 구체적인 내용은 아래와 같습니다.1. 추천할 배너를 더 잘 이해하기 GIGO(Garbage In, Garbage Out) 라는 말처럼, 좋은 모델을 학습하기 위해서는 양질의 표현(Representation)을 추출하는 것이 매우 중요합니다. 기존에도 배너 타이틀, 연관 카테고리, 브랜드, 라벨 등 다양한 메타 데이터가 DB에 저장되어 있으나, 이 정보들은 운영 목적에 따라 적재된 데이터이기 때문에, 추천 모델 관점에서 일관된 기준을 유지하기 어렵다는 한계가 있었습니다.이러한 문제를 극복하기 위해, 저희는 사용자 행동 로그를 활용한 표현(Representation) 학습 방식으로 접근했습니다. 일정 기간 동안 수집된 유저의 배너 및 상품 클릭 이력을 기반으로 이종 그래프(Heterogeneous Graph)를 구성하고, 이를 GraphSAGE 알고리즘에 적용했습니다. 이 과정을 통해 사용자 탐색 패턴을 반영한 배너 및 상품 임베딩(Embedding)을 생성할 수 있었으며, 이렇게 추출된 임베딩은 모든 노출 가능 배너 후보군에 대해 계산되어 이후 모델 학습의 입력 피처(Input Feature)로 활용됩니다.2. 클릭 확률을 계산하기 위한 모델 학습하기배너 추천 시스템의 핵심은 각 사용자에게 적합한 배너를 찾아주는 것 입니다. 저희는 이 문제를 많은 추천 시스템에서 채택하는 방식처럼 CTR(Click-Through Rate) 예측 문제로 정의했고, 이를 해결하기 위해 DeepFM과 Two-Tower 모델을 활용했습니다.앞선 단계에서 HGNN을 통해 학습된 고품
2025.06.01

좋아요

별로에요