유다현 프로필 사진

유다현

개발자

Contact

사용자의 서비스 경험을끝까지 따라가는 개발자

PLAY STYLE

  1. PASSION · 목표를 향한 분명하고 유연한 열정
    PASSION

    사용자의 다음 행동까지 설계

    기능보다 다음 단계로 이어지는 서비스 흐름을 먼저 봅니다.

  2. COLLABORATION · 놀라움과 새로움을 탄생시키는 협업
    COLLABORATION

    협업 흐름을 먼저 정리

    역할과 상태를 맞춰 다음 사람이 이어갈 결과를 남깁니다.

  3. AUTONOMY · 내가 중심에 선 주도
    AUTONOMY

    반복 문제를 구조로 해결

    임시 대응보다 원인을 나누고 다시 생기지 않을 방식을 찾습니다.

  4. PERSISTENCE · 꾸준함으로 경지에 오르는 끈기
    PERSISTENCE

    상태가 돌아갈 때까지 확인

    오류를 고친 뒤 사용자에게 돌아간 결과까지 점검합니다.

  5. TRUST YOURSELF · 나를 믿고 나아가는 확신
    TRUST YOURSELF

    근거로 판단하고 책임

    테스트와 측정으로 확인한 선택을 실행하고 다시 검증합니다.

Tech Stack

Java

Spring Boot

Spring Security

Redis

SQL

React

TypeScript

01

SCROLL

02

개발의 흐름

개발자로서의 여정

  1. 2021.03–2025.08

    동국대학교

    경영정보학과 · 융합소프트웨어

  2. 2022.03–2022.12

    멋쟁이사자처럼 10기

    HTML · CSS · 자바스크립트 웹 프로젝트

  3. 2023.01–2023.02

    부스트코스 코칭스터디 9기

    인공지능 기초 다지기 · 6주 코칭스터디 수료

  4. 2023.03–2023.12

    IT 소모임장 'ProMIS'

    신입생 대상 프로그래밍 스터디 기획 및 운영

  5. 2023.03–2024.02

    GDSC(Google Developer Student Clubs) 1기

    앱 개발 프로젝트 · 팀 협업

  6. 2024.09–2025.02

    University of Lancashire

    영국 교환학생 · 최우수 성적

  7. 2025.03–2025.06

    구름톤 유니브 4기

    개발자 커뮤니케이션

  8. 2025.07–2026.06

    삼성청년SW·AI 아카데미 14기

    자바 · 스프링 기반 백엔드 개발

  9. 2026.08

    한화금융캠퍼스 15기

    금융 실무 교육 · 현직자 멘토링

NHN

쌓아온 경험을,
NHN에서 이어가겠습니다.

03

Selected work

Project Store

서비스의 핵심 흐름과 제가 맡은 범위를 간략히 정리했습니다.

CapSure 월 보험료 입력과 캡슐 구성이 움직이는 애니메이션 화면
CapSure 대시보드가 표시되는 애니메이션 화면

구독형 보험 프로세스 시뮬레이터

CapSure
01 / 04

결제와 계약의 상태를 끝까지 맞추다.

월 단위로 보험을 구성하고 구독하는 서비스입니다. 납입, 청구, 지급, 계약 유지가 한 흐름으로 이어지도록 설계했습니다.

  • Java 21
  • Spring Boot 3.5.11
  • MyBatis 3.0.5
  • PostgreSQL
  • React 19
  • Toss Payments SDK 2
팀 구성
5명, 백엔드 3명, 프론트엔드 1명, 인프라 1명
담당 범위
FE Lead · Backend. 상품 선택 UI와 결제·계약 복구 흐름
PROBLEM 01결제 복구 · 멱등성

오류가 났다고, 결제를 다시 승인하지 않습니다.

외부 승인과 내부 저장을 구분하고, 같은 주문의 결과를 확인해 계약까지 복구했습니다.

PG 승인 완료 뒤 내부 저장이 실패했을 때 기존 주문을 대사해 계약과 이벤트까지 복구하는 시스템 흐름
복구의 기준승인은 반복하지 않고, 끊긴 내부 처리를 이어갑니다.

수납 오류의 복구 완료 기준을 응답 성공이 아니라 계약 반영까지 잡는 관점입니다.

PROBLEM 02계약 효력 · 시간 경계

배치가 늦어도, 계약 판단은 달라지지 않게.

입금 기록과 계약 효력을 분리하고, 수납 순간에도 같은 만료 규칙을 적용했습니다.

유예 종료 시점을 기준으로 수납과 실효 배치가 동일한 계약 상태를 판단하는 시스템 흐름
변하지 않는 원칙입금은 기록하되, 계약을 자동으로 되살리지 않습니다.

배치 실행 순서와 무관하게 같은 업무 기준으로 계약 상태를 판단하는 관점입니다.

회원가입 과정에서 연애 목표와 데이트 스타일을 선택하는 Roundy 취향 분석 화면
01회원가입 취향 분석

등록 사진과 실시간 촬영을 대조한 뒤, 마스킹 대화와 상호 선택을 거쳐 얼굴을 공개합니다.

얼굴 인증과 마스킹 기반 미팅

Roundy
02 / 04

얼굴 인증으로 신뢰를 더한 온라인 로테이션 매칭 서비스.

실시간으로 상대를 만나고, 실루엣으로 먼저 대화합니다. 서로 선택하면 시간이 흐를수록 마스킹이 풀리며 얼굴을 확인합니다.

  • Java 21
  • Spring Boot 3.5.9
  • Redis와 Lua
  • MySQL
  • React 19
  • TypeScript 5.9
  • OpenVidu 2.32
팀 구성
6명 · FE 1 · BE 3 · AI 1 · INFRA 1
담당 범위
매칭, 인증, 방 접근 권한 보강
PROBLEM 01race condition · 상태 정합성

늦게 도착한 이전 방 정리 요청이 새 방까지 삭제했습니다.

매칭 대기열에서 이전 방을 정리하는 과정이 동시에 들어온 새 세션 대기열 등록을 삭제하는 경쟁 상태(Race Condition)를 발생시켰습니다.

01 · PROBLEM

매칭이 끝난 사용자가 이전 방(A)의 종료 요청을 보냈지만, 네트워크 지연으로 요청이 늦게 도착했습니다.

지연 자체가 삭제를 만든 것은 아닙니다.
요청의 도착 순서가 뒤집힌 뒤, 과거 정리 요청이 최신 currentRoom=B를 조건 없이 삭제하면서 race condition이 발생했습니다.

새 세션 B 배정과 늦은 이전 세션 A 정리 요청이 Redis currentRoom에서 충돌하고, 현재 방 비교로 삭제를 막는 흐름

02 · DECISION

  • 현재 방과 대상 roomId가 일치하는 경우에만 삭제한다.
  • 일치하지 않으면 삭제하지 않고 새 방을 유지한다.
  • 비교와 삭제를 Redis Lua 안에서 원자적으로 처리한다.

03 · BUILD

Redis Lua로 인증 소비·큐 등록·방 배정을 묶었듯이, cleanup-room.lua에도 현재 매핑이 정리 대상 roomId와 같은 때만 삭제하는 가드를 추가했습니다.

local currentRoom = redis.call('GET', key) -- 현재 방 조회
local targetRoom = ARGV[1] -- 정리 요청 대상

if currentRoom == targetRoom then -- 여전히 같은 방일 때만
  redis.call('DEL', key) -- 삭제
  return 1
else
  return 0 -- 새 방이면 유지
end

04 · EVIDENCE

수정 전에는 100번 중 100번 새 방이 유실됐지만, 가드 적용 후에는 100번 중 0번으로 재현되지 않았습니다.

격리 Redis 8.4.0 · 모의 JWT/DB · 조건별 100회 로컬 시험
PROBLEM 02인증 결과 · 소유권과 일회성

인증 성공은 본인만, 한 번만 사용할 수 있게.

얼굴 인증이 성공해도, 그 결과를 다른 사람이 사용하거나 여러 번 재사용할 수 있는 문제가 있었습니다.인증 결과의 소유권을 사용자에게 귀속시키고, 단 한 번만 사용할 수 있도록 설계하고 구현했습니다.
등록 사진과 실시간 촬영을 대조하는 Roundy 얼굴 인증 화면
등록 사진과 실시간 촬영을 대조하는 얼굴 인증
Redis

Redis Gate

verify:{userId}:{requestId}
  • 소유자가 본인인가?요청한 userId와 일치하는가?
  • 상태가 VERIFIED인가?아직 소비되지 않은 상태인가?

인증 결과 상태 생명주기

  1. •••PENDING인증 진행 중
  2. VERIFIED본인 확인 완료1회 소비
  3. ×CONSUMED사용 완료 · 재사용 불가
타인 요청BLOCKED

다른 사용자의 요청에서
해당 토큰 사용 불가

재소비BLOCKED

이미 사용된 토큰은
다시 사용할 수 없음

늦은 완료BLOCKED

만료된 토큰은
인증 완료 처리 불가

01 · PROBLEM

성공 여부만 검사하면 타인의 요청이나 겹친 요청이 같은 결과를 사용할 위험이 있습니다.소비 후 늦은 완료 응답이 결과를 되살리는 조건도 점검했습니다.

02 · DECISION

PENDING은 소비하지 않고, VERIFIED만 한 번 사용하도록 제한했습니다.

03 · BUILD

verify:{userId}:{requestId}에 결과를 귀속시켰습니다.완료는 PENDING에서만, 소비는 VERIFIED에서만 허용하는 Lua 전이로 재사용을 막았습니다.

if owner == userId && state == VERIFIED then
  consume(result) -- 1회 소비
end

04 · EVIDENCE

16개 소비 요청 중 1개만 성공.타인 소비와 재소비는 실패.

실제 Redis · 모의 DB · 8개 워커에 16개 소비 요청 제출

01 자료를 익스텐션에 넣고02 지식 나무로 모아보기
03 TIL로 정리하고04 유사 지식을 확인하며 지식 성장하기
텍스트, 이미지, 링크를 드래그해 지식을 저장하는 SAN 크롬 확장 프로그램 화면
저장한 자료가 카테고리별 지식 나무로 모인 SAN 화면

흩어진 자료가 하나의 지식 나무로

저장한 지식을 바탕으로 오늘의 학습을 정리하는 SAN TIL 화면

저장한 지식을 오늘의 TIL로

학습 기록과 활동을 한눈에 보는 SAN 마이페이지 화면

쌓인 기록을 마이페이지에서

크롬 확장 프로그램 기반 지식 관리

SAN
03 / 04

흩어진 자료를, 다시 쓰는 지식으로.

크롬 확장 프로그램으로 저장한 자료를 검색, TIL, 지식 카드로 연결하는 서비스입니다. AI 정리 기능도 원문 근거를 남긴 상태에서 검토할 수 있도록 설계했습니다.

  • Java 21
  • Spring Boot 3.5.14
  • Spring Data JPA
  • PostgreSQL
  • Redis
  • React 18.3
  • TypeScript 5.9
팀 구성
7명 · FE 1 · BE 3 · AI 2 · INFRA 1
담당 범위
비동기 감사 추적, 로그인 브리지, AI 요약 병렬화
Chrome Web Store에서 SAN 보기
PROBLEM 01비동기 작업 · 감사 추적

비동기 작업이 요청 맥락을 잃지 않도록.

작업이 큐로 넘어가도 누가 요청했고 어떤 경로로 실행됐는지 남겨야 했습니다.요청 스냅샷을 워커에서 복원하고 시작·성공·실패를 같은 추적 흐름으로 기록했습니다.

Request Snapshot을 Queue와 Worker로 전달하고 Audit Log를 남긴 뒤 이전 Context를 복원하는 비동기 감사 흐름

01 · PROBLEM

비동기 작업은 요청 스레드를 벗어나 실행되기 때문에 actorUserId, traceId, IP, User-Agent 같은 요청 맥락이 사라질 수 있었습니다.

02 · DECISION

큐에 넣는 순간 감사에 필요한 값을 스냅샷으로 고정하고, 워커가 실행될 때 같은 컨텍스트를 복원하기로 했습니다.

03 · BUILD

AuditedAsyncJobRunner가 요청 스냅샷을 전달하고 START·SUCCESS·FAILURE를 같은 traceId로 기록합니다.finally에서는 워커가 사용하기 전 컨텍스트를 다시 복원합니다.

04 · EVIDENCE

요청 스냅샷 복원, 감사 이벤트 기록, 이전 컨텍스트 복귀를 확인.
PROBLEM 02로그인 브리지 · 토큰 노출

JWT를 URL에 남기지 않는 익스텐션 로그인 전환.

채널을 넘나드는 로그인에서 장기 토큰을 주소에 실어 보내지 않았습니다.짧게 살아 있는 1회용 티켓으로 교환하고 읽는 순간 소비되도록 제한했습니다.

Dashboard에서 발급한 One-Time Ticket을 Redis에 저장하고 getAndDelete로 한 번만 소비해 Chrome Extension 로그인으로 교환하는 흐름

01 · PROBLEM

대시보드와 익스텐션 사이에서 JWT를 URL로 전달하면 브라우저 기록과 리퍼러 등에 장기 토큰이 남을 수 있었습니다.

02 · DECISION

URL에는 짧게 살아 있는 티켓만 전달하고, 교환 시 access token과 source client type을 다시 확인하기로 했습니다.

03 · BUILD

SecureRandom 32-byte URL-safe 티켓을 Redis에 저장했습니다.Dashboard → Extension은 TTL 2분, Extension → Dashboard는 TTL 30초이며 getAndDelete로 조회 즉시 소비합니다.

04 · EVIDENCE

유효한 티켓은 한 번만 소비하고, 만료·재사용 티켓은 거절.
PROBLEM 03AI 요약 · 지연 측정

실제 AI 호출을 병렬화해 처리 시간을 약 59% 단축했습니다.

같은 모델과 같은 입력으로 순차 처리와 병렬 처리를 짝지어 비교했습니다.평균과 p95가 함께 줄어드는지 확인해 카드 요약 단계의 개선만 증명했습니다.

01 · PROBLEM

카드 3개의 AI 요약을 순차 호출하면서 각 응답 대기가 그대로 누적됐습니다.카드 요약 단계만 평균 17.68초까지 길어졌습니다.

02 · DECISION

모델·입력·카드 수를 동일하게 고정했습니다.실행 순서의 영향을 줄이기 위해 AB/BA를 균형 배치한 paired benchmark로 비교했습니다.

03 · BUILD

서로 독립적인 카드 요약 3건을 CompletableFuture로 동시에 실행했습니다.gpt-5-mini, 합성 카드 3개, 10쌍·총 20회 조건을 동일하게 유지했습니다.

04 · EVIDENCE

평균 17.68s → 6.92s, p95 21.30s → 8.59s, 중앙값 기준 59.45% 단축.

성공 10/10 · 카드 요약 단계만 측정 · 전체 TIL·UI 응답 시간 제외

같은 입력의 카드 요약을 위쪽 Sequential과 아래쪽 Parallel로 비교한 Paired Benchmark
하루 한 번 자가 진단을 시작하는 다시봄 iPhone 목업 화면표정과 한쪽 눈 윙크를 안내하는 다시봄 iPhone 목업 화면가까운 병원 정보를 지도에 표시한 다시봄 iPhone 목업 화면

AI 기반 뇌졸중 위험 신호 확인 앱

다시봄
04 / 04

AI 분석 뒤, 결과와 가까운 병원 정보를 바로 확인합니다.

얼굴·음성·설문으로 뇌졸중 위험 신호를 확인하고, 결과와 병원 탐색으로 이어지는 모바일 앱입니다.

  • React Native
  • Spring Boot
  • MySQL
  • AI 분석 API
팀 구성
4명 · FE 1 · BE 1 · AI 2
담당 범위
서비스 기획·UI/UX, React Native 화면과 카메라·음성·지도·차트 연동
PROBLEM 01다시봄민감한 입력 · AI 응답 검증

분석에 쓴 원본과, 기록에 남길 결과를 분리했습니다.

프로젝트에서 원본 업로드 경로를 제거하고, 입력과 AI 응답을 각각 검사하는 경계를 검증했습니다.

호출 전 검사 → 저장 전 검사 서비스의 진단 처리 경로
  1. 01

    얼굴·음성 입력

    영상 ≤ 50MB / 음성 ≤ 20MB

    형식·용량 검사
  2. 02

    외부 AI 분석

    요청 메모리에서 원본 전달

    판정값·확률 검사
  3. 03

    파생 결과 기록

    서비스 저장소에 원본 업로드 안 함

    허용된 응답만 저장
호출 전용량 초과 · MIME 불일치AI 호출하지 않음
저장 전판정값 2 · 확률 1.3진단 기록 저장하지 않음
서비스 저장 기준원본 파일 대신,
검사한 파생 결과만 남깁니다.
01 · PROBLEM

기존 진단 경로는 AI 호출 전 얼굴·음성 파일을 S3에 올렸습니다. 분석 뒤에도 원본을 보관해야 하는 이유는 확인되지 않았습니다.

02 · DECISION

원본은 분석에 전달하되 서비스 저장소에는 남기지 않고, 허용 범위의 판정값·확률만 기록하는 기준입니다.

03 · BUILD

S3 업로드 의존을 제거했습니다. 형식·용량은 AI 호출 전에, 판정값 0/1과 유한한 확률 0~1은 저장 전에 검사합니다. 외부 오류는 일반화된 코드로 반환합니다.

04 · EVIDENCE잘못된 입력은 AI 호출 전 차단. 판정값 2·확률 1.3 응답은 기록 저장 차단.

Mockito · H2 · 전체 백엔드 테스트 11건 통과

APPLICATION

민감한 자료를 분석에 사용하는 목적과 서비스에 보관할 대상을 분리하는 관점입니다.

검증 조건과 범위 보기

2026-09-19 백엔드 테스트를 강제 재실행해 5개 클래스의 11건이 실패·오류·스킵 없이 통과했습니다. 영상 mp4/mov 최대 50MB, 음성 wav/pcm/m4a 최대 20MB, 전체 multipart 요청 75MB 기준으로 검사합니다. 검증 대상은 서비스의 입력·응답 처리와 저장 경로입니다.

PROBLEM 02다시봄기록 조회 · 소유권 검사

로그인했어도, 다른 사람의 기록은 열 수 없게.

진단 ID만 찾던 조회를 사용자 ID까지 함께 확인하도록 바꾸고, 타인 요청의 차단 범위를 검증했습니다.

기록 ID + 인증 사용자 ID 소유권 확인 후 상세 조회
이전진단 ID만 조회변경진단 ID AND 사용자 ID
기록 소유자와 요청자가 일치

본인 기록

상세 조회 진행

결과와 연관 정보로 연결

기록 소유자와 요청자가 불일치

타인 기록

404

연관 병원 조회도 중단

상세 조회의 완료 조건“있는 기록인가?”가 아니라
“내 기록인가?”까지 확인합니다.
01 · PROBLEM

진단 ID만 조회하면 요청자와 기록 소유자가 연결되지 않습니다. 다른 사람의 ID를 아는 요청이 상세 기록으로 이어질 여지가 있었습니다.

02 · DECISION

기록의 존재 여부보다 소유권을 먼저 확인합니다. 일치하지 않으면 연관 병원 정보 조회도 진행하지 않습니다.

03 · BUILD

인증 사용자 ID를 컨트롤러에서 서비스로 전달합니다. findByIdAndUserId로 기록을 조회하고, 일치하지 않으면 DIAGNOSIS_NOT_FOUND로 처리합니다.

04 · EVIDENCE타인 기록 요청은 404. 이때 연관 병원 저장소 조회도 호출되지 않음을 확인.

Mockito · 소유권 서비스·컨트롤러 테스트 각 1건

APPLICATION

개인별 청구·건강 관련 기록을 조회할 때 로그인 여부와 대상 데이터의 소유권을 따로 검사하는 관점입니다.

검증 조건과 범위 보기

DiagnosisQueryServiceTest에서 타인 진단 ID 요청의 404 응답과 병원 저장소 미호출을 확인했습니다. DiagnosisControllerTest에서는 인증 사용자 ID가 서비스로 전달되는지 검사했습니다. 서비스·컨트롤러 단위의 검증입니다.

병원 탐색 화면 보기
병원 탐색 원본 크게 보기 ↗
다시봄 지도, 병원 상세와 검색 목록 원본 캡처