
TOPIK SSAM(토픽쌤) 앱
Unity 기반 멀티 플랫폼 학습 앱입니다.
- Unity 기반 멀티 플랫폼 앱 클라이언트 전담 개발
- 백엔드 API 일부 개발
- Google Play · App Store 출시
- Unity
- C#
요약은 홈에서 보실 수 있습니다. 이 페이지는 실무 프로젝트별 담당 범위와 기술, 개인 프로젝트의 구조도, 운영 중 마주친 문제와 해결 과정을 자세히 적은 노트입니다.
아래로 스크롤하여 읽기팀 구성과 그 안에서 제가 맡은 범위를 적었습니다. 출시 이후 지표는 개발사에 공유되지 않아, 확인할 수 없는 수치는 적지 않았습니다.

Unity 기반 멀티 플랫폼 학습 앱입니다.

React Native 앱과 React 웹앱으로 구성한 AI 회화 서비스이며, 응답은 스트리밍으로 받습니다.

AI 글쓰기 웹앱과 이를 운영하는 관리자 웹으로 나뉜 프로젝트입니다.

Unity 클라이언트와 NestJS 백엔드, 웹앱으로 구성된 학습 서비스입니다.
비대면 영어학습환경을 위한 스마트홈 기반 인공지능 학습친구(AILA) 플랫폼 기술개발 과제입니다.
WebSquare5 플랫폼으로 기존 레거시 시스템을 웹 표준 UI/UX 환경으로 전환하고 최적화했습니다.
/api/generate의 생성-검수 파이프라인입니다. MBTI 판정은 규칙 기반 코드가 확정하고, LLM은 그 근거를 설명하는 일만 맡습니다. 설명은 코드 검사와 AI 검수 1회를 거칩니다.
코드 검사나 AI 검수에서 불합격이면 04에서 03으로 되돌아갑니다. 누적 피드백을 붙여 수정 생성하며, 생성 최대 3회 · 요청당 Gemini 호출 최대 6회 · 45초 소프트 마감(함수 최대 60초) 안에서 가장 나은 후보를 반환하며, 끝내 통과하지 못하면 미검증으로 표시합니다.
업무 데이터를 다루는 도구라서, AI가 보는 정보를 컬럼 제목과 샘플 몇 줄로 한정했습니다.
이 저장소의 git 기록에서 가져왔습니다. 해시는 커밋 번호이고, 아직 커밋하지 않은 항목은 파일명으로 적었습니다.
쉽게 말하면DB 연결이 바닥나 서비스가 멈추던 문제를 고쳤습니다.
증상서버 선택 타임아웃(MongoServerSelectionError)이 운영에서 발생했습니다.
원인로그인 도입 후 DB 접근 경로가 늘었는데, 운영 환경에서는 global 싱글톤을 쓰지 않아 모듈 평가마다 새 연결 풀이 생겼고 Atlas M0의 연결 상한을 넘었습니다.
해결dev와 prod 모두 global 싱글톤 하나만 재사용하고, 연결 실패 시 캐시를 비워 다음 요청에서 재시도하게 했습니다.
쉽게 말하면DB가 느려져도 페이지 전체가 죽지 않도록 대기 시간과 오류 처리를 손봤습니다.
증상DB 연결이 지연되면 페이지가 Application error로 끝났습니다.
원인기본 연결 대기가 30초라 Vercel 실행 제한을 넘었고, 연결 실패가 unhandled rejection으로 남았습니다. 레이아웃이 JWT에 이미 있는 값을 DB에서 다시 조회하기도 했습니다.
해결타임아웃을 8초로 줄이고 실패를 로그와 재시도 Promise로 처리했으며, 레이아웃의 DB 조회는 JWT 세션으로 대체했습니다.
쉽게 말하면관리자가 승인해 줘도 다시 로그인해야 하던 불편을 없앴습니다.
증상관리자가 승인해도 사용자가 다시 로그인하기 전까지 접근할 수 없었습니다.
원인isApproved와 isAdmin이 로그인 시점의 JWT 값으로 고정되어 있었습니다.
해결jwt 콜백이 300초(기본)마다 DB에서 재조회하고, 승인 대기 화면에 즉시 재확인 버튼을 두었습니다. DB 오류 시에는 기존 값을 유지하고, API의 승인 기능은 요청마다 확인하되 조회 실패 시 503을 반환합니다.
쉽게 말하면AI를 부를 때마다 줄어드는 무료 사용량이 순식간에 바닥나지 않도록 횟수와 시간에 상한을 뒀습니다.
증상재생성 루프 때문에 한 요청이 여러 번 Gemini를 호출해, 공개하면 무료 한도가 빠르게 소진될 수 있었습니다.
원인검수 루프에 호출 횟수와 시간 상한이 없었고, 같은 입력도 매번 새로 호출했습니다.
해결요청당 호출을 최대 6회, 시간 예산 45초(함수 최대 60초)로 제한하고 MongoDB 결과 캐시, IP별 rate limit, 일일 호출 한도를 추가했습니다. 모델 체인 폴백도 넣었습니다.
쉽게 말하면오류가 나도 이유를 알 수 없던 것을, 원인이 보이게 바꿨습니다.
증상Gemini 호출이 실패하면 이유 없이 500만 돌아왔습니다.
원인POST 핸들러에 try/catch가 없었고, 'GenAI 호출 실패'로 실제 오류가 가려졌습니다.
해결핸들러 전체를 감싸 실제 오류 메시지를 응답과 로그에 포함해, 잘못된 모델명이나 API 키 누락을 운영에서 바로 진단할 수 있게 했습니다.
쉽게 말하면코드에 쌓여 있던 경고 99건을 동작은 그대로 두고 모두 정리했습니다.
증상프로젝트 전반에 쌓인 린트 오류와 경고가 99건이었습니다.
원인미사용 코드, any 타입, 내부 링크의 a 태그 사용 등이 누적되어 있었습니다. 일부는 Three.js·R3F와 애니메이션 때문에 React hooks 규칙을 그대로 지킬 수 없는 코드였습니다.
해결동작 변경 없이 정리하고, 고칠 수 없는 hooks 규칙은 사유를 적은 국소 eslint-disable로 처리했습니다.
쉽게 말하면AI가 만든 화면 효과 코드의 숨은 오류를 검수 과정에서 잡아 고쳤습니다.
증상동작 줄이기(reduced motion) 설정이 켜진 환경에서 홈 페이지를 열면 하이드레이션 불일치 콘솔 오류가 났습니다.
원인AI 에이전트가 작성한 Reveal 컴포넌트가 useReducedMotion() 값으로 렌더 트리를 나눴습니다. 서버는 사용자 설정을 알 수 없어 null(애니메이션 div)을, 클라이언트는 true(일반 div)를 렌더해 서로 달랐습니다.
해결항상 같은 motion.div를 렌더하고, 동작 줄이기 처리는 MotionConfig reducedMotion="user"에 맡겼습니다. (components/Home/Reveal.tsx)