크래프톤 정글의 마지막 과정인 '나만의 무기 만들기' 프로젝트가 최종 발표를 앞두고 있습니다.
한 달을 꽉 채워서 치열하게 개발하고, 오늘이 되어서야 핸드오프 했습니다!
(실은 자잘한 버그가 아직 남아있긴 합니다 🥲)
오늘은 TIO(try it on)의 기획부터 개발, 기술적인 챌린지를 정리해보려 합니다.
프로젝트는 6월 19일에 시작되었는데요, 돌아오는 토요일 7월 26일이 데모데이랍니다.
5주가 조금 넘는 기간동안 진행하였습니다.
일정은 이러했습니다!

서비스의 주요 기능은 MVP 기간에 전부 완성하였고
실제 서비스와 유사한 정도로 성능을 끌어올리고, 사용자의 경험을 개선하는 과정은 폴리싱 기간에 진행하였습니다.
기존에 진행했던 프로젝트에 비하여 널널하다고 생각했지만 오산이었죠.. 실제로 서비스하고있는 수준으로 구현해야했기에 폴리싱 단계에서도 무척이나 애를 먹었습니다.
우선 기획 단계부터 정리해볼까 합니다!
우리 팀은 첫 회의 시간에 어떤 불편함을 겪었는지, 어떤 서비스가 나에게 필요한지 나눴습니다.

어느새 한달이 훌쩍 지나버린 첫 회의 날의 사진
우선 저는 학교나 동아리에서 프로젝트를 진행할 때, 기획 단계에서 탈락되었던 아이디어들을 다시 한 번 검토해보았어요.
그 중, 여전히 매력적으로 느껴지는 아이디어를 여러 개 주워갔습니다.
이런 저런 말들로 아무말 대잔치 하다보면 괜찮은 아이디어가 하나씩 툭 튀어나오기 마련이니까요.
최초에 우리 팀장님이 제안했던 아이디어는 동성 친구, 취미 생활에 포커싱된 소개팅 앱이었어요.
의도와는 달리 악용될 가능성을 배제하기 어려웠다고 판단하여, 다른 아이디어도 고민해보기로 했습니다.
제가 준비한 아이디어들이 꽤나 주목받았습니다.
1. 옷을 살 때, 자신과 비슷한 체형으로 미리 착장 확인할 수 있는 서비스
2. 피드백을 남겨주는 서비스
3. 위치 기반 도슨트 서비스(관광지 혹은 미술 작품)
4. 펫 로스 증후군을 극복하기 위한 가상 반려동물 서비스
5. 에브리타임처럼 비는 시간을 표시하여 서로의 일정을 공유하는 서비스
크게 이렇게 다섯 가지 아이디어를 가지고 갔고, 반응이 좋았던 아이디어는
1번 착장 서비스, 2번 피드백 서비스, 3번 도슨트 서비스였습니다.
(번외로 게임을 만들어보자는 의견도 있었지만, 저희 개개인이 원하는 포트폴리오가 될 것으로 생각되지 않아 과감히 포기했습니다.)
기획적으로는 팀원 모두가 피드백 서비스에 끌려, 이 쪽으로 기울어졌으나, 기술적으로 보여줄 수 있는 점이 적겠다는 코치님의 우려로 착장 서비스로 결정하게 되었어요.
이 날, 팀 규칙도 세우고 화기애애하게 첫 회의를 마무리했던 기억이 아직 생생하네요 😆

이 규칙은 제가 제안했었는데, 잘 받아들여준 우리 팀원들에게 감사한 마음이 큽니다...
여담이지만 개발에 투입된 이후에는 거의 매일 아침 해가 뜨는 걸 보고 퇴근하는 게 일상이 되었어요 ☠️
아이디어가 정해진 후에는 더욱 구체화하고 근거를 마련하는 데 시간을 투자했습니다.

출처: https://m.cafe.daum.net/ok1221/9fQk/118826?svc=topRank
이 짤을 보신 적 있나요?
저는 이 짤을 보고 '헉... 똑똑하다.. 좋은데?'라고 생각했던 기억이 있었는데요.
핫핑크 셔츠가 예뻐보여서 샀다가 제 퍼스널 컬러와 정~ 말 안 어울려서 한 번도 못 입은 옷도 있거든요.
그래서 늘 갈증이 있었어요.
'옷을 사기 전에 내 얼굴, 내 체형에 한 번 피팅해보고싶다!'
이런 갈증이요
트레이닝복만 입다가, Y2K 스타일에도 도전해보고 싶을 때가 있잖아요?ㅎ ㅎ
구입 전에 나와 잘 어울릴지 예측해볼 수 있다면 얼마나 좋을까요?
저희 서비스 Try It On은 이런 갈증에서 출발했어요.
기획 단계에서 기술 서칭을 해보았는데, 이런 기술이 있었습니다.

기술 출처: https://huggingface.co/spaces/Kwai-Kolors/Kolors-Virtual-Try-On
인물 사진과 의류 사진을 업로드하면 착장 사진으로 반환해줍니다!
사진을 보시다시피, 우려했던 것 보다 훨씬 자연스러웠어요.
이 모델을 가져다 쓰면 되겠다! 생각했죠.
그렇게 첫 번째 초핵심 기능은 Try on(가상 피팅) 기능이 되었습니다.
이 기술을 우리 서비스에 적용시키는 데는 수많은 우여곡절이 있었지만, 뒤에서 이야기하도록 할게요.
우선 이 기술은 사용자가 실제 제품을 착용하지 않고도 가상으로 착용해볼 수 있도록 하는 기술입니다.
유저의 사진 데이터가 input으로 들어왔을 때, 딥러닝 기반의 포즈 추정과 상/하의를 각각 마스킹한 이미지를 생성합니다.
그 이후에 착장하는 의상 사진 데이터가 input으로 들어오면 기존에 생성되었던 포즈 이미지, 상/하의 마스킹 이미지가 결합됩니다. 가상 의류가 몸에 맞게 구겨지거나 주름지는 등의 옷감의 물리적 특성을 반영한 CV 기술이 복합적으로 사용됩니다.
fitdit(https://huggingface.co/BoyuanJiang/FitDiT)이라는 오픈소스 모델을 사용하였습니다.
결과물을 바로 보실까요?!
| 원본 | 상/하의 착장 | 상의 착장 |
|---|---|---|
![]() | ![]() | ![]() |
어떤가요? 스타일을 미리 확인하는 데 충분히 도움이 될 것 같지 않나요?😎
처음에는 한 번의 착장 시도에 1분이 넘는 시간이 걸렸는데, 모델을 경량화하고 파이프라인을 변경하면서 최대 10초의 지연시간으로 최적화 하는 데 성공했습니다!
(다만, AI 모델이다보니.. GPU 서버를 사용해야해서 비용이 만만치 않다는 점..)
패셔니스타들이 모이는 이 공간, TIO에서 이렇게 가상 피팅해본 착장 이미지를 자유롭게 공유하는 소셜 기능이 있으면 좋겠다는 생각이 피어났습니다.
그래서 두 번째 기능인, 커뮤니티 기반의 패션 스토리 기능을 추가하였습니다.
이전에 우선! 마음에 드는 착장은 옷장에 추가하여 보관할 수 있도록 기획하였어요.

이런 식으로요!
마음에 드는 상품은 찜을 해두듯, 마음에 드는 착장은 옷장에 담아둡니다.
그리고 이 중에서도 다른 사람들과 공유하고싶다면, 스토리로 공유를 할 수 있습니다.
인스타그램 스토리처럼요!
저희 TIO는 패션 플랫폼이다보니, 그대로 스토리에 올리는 것 보다는 미적 요소를 가미하면 좋겠죠?
배경을 제거하고 마음에 드는 배경색을 선택하여 업로드할 수 있는 기능을 포함하였습니다.


이렇게 스토리를 커스텀하여 업로드할 수 있어요!
| 회색 배경 | 흰색 배경 | 핑크 배경 |
|---|---|---|
![]() | ![]() | ![]() |
그런데 뭔가 아쉬운 마음이 한 켠에 남아있었어요.
커뮤니티인만큼 MZ스러운 재미요소를 듬뿍 포함시키고 싶은 마음, 하나의 뭔가 다른 임팩트 말이죠!!
고민하던 중, 좋은 아이디어가 떠올랐습니다. 이런 설문조사 많이 해보셨죠?

출처: https://v.daum.net/v/cHr3DmlxO5?f=p
백이면 백, 이렇게 포스트잇을 붙이는 설문조사에는 터무니없는 내용이 적힌 포스트잇도 많이 보입니다.
보통은 번화가에서 친구들과 편안하게 대화를 나누며 가볍게 참여하였던 것 같아요. 그런 편안한 분위기가 머릿 속으로도 그려져서 친근하게 다가오는 듯 합니다.
이미 붙어있는 포스트잇에 덧붙여 나의 의견을 피력하기도, 특정 포스트잇에 댓글을 다는 형식이 되기도 합니다.
한 장의 종이로 설문을 진행하였다면 딱딱한 답변만 있었을텐데 말이죠!
말이 길어졌지만, 결론은!
스토리의 댓글을 포스트잇을 붙이는 형태로 남기자!
.
.
패션 컨텐츠이기 때문에 상의 혹은 하의에 코멘트를 남기고 싶을 수도 있고, 스타일 전체에 코멘트를 남기고 싶을 수도 있겠다고 생각했어요.
위치까지 고려된 댓글을 남길 수 있다면 컨텐츠가 더욱 풍부해지지 않을까? 생각했고,
'포스트잇으로 해보자!' 라는 의견이 나왔을 때 엄청난 쾌감을 느꼈답니다.. 너무너무 매력적이지 않나요?!
이 스토리 기능은 제가 맡아 구현하였습니다.(🤭)
한 번 보실까요?!
| 원본 | 상/하의 착장 |
|---|---|
![]() | ![]() |
이런 식으로 포스트잇을 남길 수 있습니다
온라인 쇼핑몰에서 사용자의 구매 욕구를 극대화하기 위해서 다양한 방법을 사용하는데요,
그 중 오늘날에는 필수적인 요소로 자리잡은 개인화 추천 기능이 있습니다.
실은 이 기능은 초기 TIO 스펙에는 포함되지 않았어요.
부가적인 요소로 고려하고 있었으나, 폴리싱 단계에서 개인적인 욕심으로 구현하게 되었습니다.
유저의 프로필을 기반으로 한 Content-based filtering과 유저의 행동을 기반으로 한 Collaborative filtering 기법을 결합한 하이브리드 추천 시스템을 적용하였습니다.
CLICK(0.5f), // 상품 클릭
WISHLIST(1.0f), // 찜
CART(2.0f), // 장바구니
BUY(3.0f), // 구입
TRYON(2.0f), // 가상 피팅
TRYONCLOSET(2.5f); // 가상 피팅 착장 저장
유저의 행동에는 다음과 같은 가중치를 부여하고, 그렇게 연산한 결과에 따라 유사한 상품을 추천하는 알고리즘을 구현하였습니다.
| 개인화 추천 | 연령대별 추천 |
|---|---|
![]() | ![]() |
또한, 특정 상품을 조회하였을 때 해당 상품과 유사한 태그를 가진 상품을 노출시키도록 하였습니다.

이처럼 맨투맨을 조회하면 유사한 상품이 추천됩니다.
TIO는 크게 이러한 기능으로 구성되었습니다.
저희는 '실제로 서비스되고있는 규모로 구현해보자' 는 목표를 가지고 있었기 때문에 상품 개수를 대폭 늘렸습니다.
[TOBE] 초기에는 1천 5백개의 상품 데이터를 무신사에서 크롤링하여 사용하였습니다.
[ASIS] 폴리싱 단계에서는 21만개의 상품 데이터로 증가시켰습니다.
이 과정에서 엄청난 난관에 부딪혔습니다..
이렇게 많은 데이터를 다뤄본 경험이 없었기 때문에 급속도로 웹사이트가 느려지는 현상을 경험했어요.
'이건 도저히 보여줄 수 없겠다..' 싶은 지경이었습니다.
DB를 최적화하는 여러 방안을 고민하게 되었어요.
이는 우리 팀원 중, 한 분이 맡아주셨는데 변경 전/후의 차이를 몸소 느낄 수 있었습니다.
DB 성능은 아무리 강조해도 지나치지 않다는 것을 깨닫게 된 계기가 되었어요 🧐
(더 자세한 이야기는 우리 팀원분의 블로그를 참고해주세요! 구름씨의 블로그)
세 가지 관점에서 성능 개선을 이루어냈습니다.
N+1 쿼리 해결 (Fetch Join)
요놈이 큰 문제였는데요, 프로젝트 전에 Lazy Loading의 중요성을 강조했었는데 그 때문인지 모든 엔티티를 설계할 때 각 테이블을 전부 Lazy하게 연결되도록 구현하였습니다.
상품이 많아질수록 이 문제는 커져갔습니다. 1개의 상품을 조회하면 관련된 모든 테이블을 조회하게 되는 문제로 1개의 상품에 101번의 쿼리가 조회되는 문제까지 발생했습니다.
Redis 캐싱
그래도 여전히 최초 로딩 속도가 만족스럽게 개선되지 않았습니다.
그래서 redis에 상품 정보를 캐싱하는 방법을 선택하였습니다. 자주 찾는 상품 데이터(상위 랭킹)를 인 메모리에서 제공하여 속도를 크게 개선하였습니다! 👏
커버링 인덱스
데이터베이스 자체적으로 테이블에 접근하는 시간을 단축시키는 방법을 도입하였습니다.
저희는 데이터베이스로 MySQL을 사용하였는데요, 쿼리가 테이블 접근 없이 인덱스만으로 조회할 수 있도록 하는 커버링 인덱스를 적용하였습니다.
앞에서 잠깐 언급했던 문제인데요, 이게 아주 심각했던 문제였어요..
저희 팀원이 5명인데 3명이 동시에 테스트해봤더니 바로 터져버리더라구요.......
심지어 1분에서 2분가량의 지연시간이 있었어요.
이렇게 가다간, 데모날 아무것도 보여줄 수 없겠다! 비상!!🚨
대대적으로 구조를 개선하게 되었어요.
기존 Try on 이미지 처리의 구조는 다음과 같습니다.

하나의 GPU 서버에서 try on 요청을 수신하고 처리합니다.
요청이 들어오는 순서대로 작업 큐에 쌓이게 되며, 대기 시간이 60초가 초과되는 시점에 timeout으로 요청이 거부됩니다.
변경한 구조는 다음과 같습니다.

Redis를 Message queue로 사용하여 try on 요청을 수신하고 저장합니다.
이후에 Redis는 AI 모델을 동작시키는 GPU 서버에 작업을 전달하여 빠른 응답시간을 보장할 수 있습니다.
당연한 말이지만, GPU 서버는 확장하는 만큼 동시 요청과 처리 속도를 개선할 수 있습니다.
문제는 여기서 끝이 아니었습니다. ㅎㅎ(ㅜㅜ)
저희는 유저가 회원가입하는 시점에 업로드한 전신사진으로 여러 의상을 착장해보는 서비스입니다.
기존에 구현되어있던 로직에는 치명적인 문제가 있었는데요,
어제 입어본 티셔츠를 오늘 입어보면 새로운 요청이 AI모델 서버로 들어갑니다. 그렇게 되면 같은 결과를 기대할 수 없고 매 요청마다 다른 결과가 반환됩니다.
이렇게 되면 1. 사용자의 혼란 유발, 2. 서버의 부하 의 문제가 추가적으로 발생합니다.
따라서,
입어본 상품은 캐싱하여 재요청시에 AI 서버를 사용하지 않고 저장된 이미지를 즉시 반환하는 구조로 변경해야합니다.

위의 decision tree를 따라 착장 정보를 캐싱합니다.
이에 따라, 두 가지 부분이 개선되었습니다.
1. 이미지 열화 문제 해결

기존에는 캐싱이 없었기 때문에 착장 요청이 계속 덧씌워지는 문제가 있었습니다.
캐싱 정책은 유저의 원본 이미지에 상/하의를 단일하게 적용합니다.
따라서, 기존에 try on 요청이 많아질수록 열화되던 이미지가 개선되었습니다.
2. AI 서버 부하 문제, 지연시간 단축
AI 서버가 try on 요청을 처리하는 데는 10초 가량 소요됩니다.
이를 캐싱한다면, S3 버킷에서 반환하는 시간이 소요되므로 즉시 반환 가능합니다.
이는 사용자에게 지연시간을 최소 10배 단축시키는 효과로 작용합니다!
더불어, 이 요청은 AI 서버로 전송되기 때문에 불필요한 요청에 대한 처리도 제외됩니다.
TIO의 여정은 아직 끝나지 않았지만, 기억이 휘발되기 전에 기록해봅니다!
추가할 내용이 생기면 계속 수정하겠습니다!
흥미롭네요