"기획부터 운영까지, AI 도구를 활용할 줄 아는 생산성 높은 개발자"
요구사항을 명확히 파악하고 다양한 리더, 팔로워 경험으로 팀과 적극적으로 소통하며 문제를 해결합니다.
회의 결정사항을 문서화하여 협업 효율을 높이고, 기술적 난관에 유연하게 대처합니다.
2025.11 - 2025.12 (2개월) | 팀 프로젝트 (5명) | 실무 인턴십
패션 블로그 자동 생성 시스템 개발 (Fashion 크롤링, AI 통합, BlogTemplate 도메인)
- Fashion 크롤링, AI 로직 및 통합, BlogTemplate 도메인 (단독)
1. 패션 E-commerce 크롤링 - 내부 API 발굴
문제 상황:
- 타겟 패션 쇼핑몰은 공개 API를 제공하지 않아서 데이터 수집 방법을 찾아야 했습니다.
해결 과정:
- Chrome DevTools Network 탭에서 사이트 상품 로딩 추적
- XHR 요청 중 JSON 응답 반환하는 내부 API 엔드포인트 발견
- Headers 분석 (User-Agent, Cookie, Referer) 후 Postman 테스트
- Java RestClient로 구현
안정적인 데이터 수집:
- 문제: 연속 요청 시 서버 부하로 차단
- 해결: 카테고리별 요청 사이 적절한 지연 시간 설정, HTTP 502/503 에러 발생 시 재시도 로직 (2초 후 최대 2회), SSH로 서버 로그 실시간 모니터링
- 결과: 안정적으로 상품 데이터 수집 완료
기술 구현:
// HTTP 에러 발생 시 재시도 로직 (2초 대기, 최대 2회)
public String fetchWithRetry(String url) throws InterruptedException {
int maxAttempts = 3;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return restClient.get()
.uri(url)
.header("User-Agent", USER_AGENT)
.header("Referer", REFERER)
.retrieve()
.body(String.class);
} catch (HttpServerErrorException e) {
if (attempt < maxAttempts) Thread.sleep(2000);
}
}
throw new CrawlingException("수집 실패: " + url);
}// PostgreSQL 배열 타입으로 다중 값 저장 (JOIN 불필요)
@Column(columnDefinition = "text[]")
private String[] colors; // ["red", "blue", "black"]
@Column(columnDefinition = "text[]")
private String[] seasons; // ["spring", "summer"]
// 단일 쿼리 조회 (PostgreSQL native query)
@Query(value = "SELECT * FROM product WHERE :color = ANY(colors)", nativeQuery = true)
List<Product> findByColor(String color);- RestClient (Spring 6.1+)로 JSON API 호출
- JSoup으로 HTML 파싱 (API로 제공되지 않는 색상 정보 추출)
2. 팀 협업 및 문서화 - 5명 팀원과 회의 기반 의사결정
팀 구성: 나 (Fashion 크롤링, AI 통합, BlogTemplate), 팀원들 (User, Auth, Blog, Dashboard, 프론트엔드)
협업 방식:
- 주간 회의에서 기술적 결정 논의 및 합의
- Slack으로 회고 및 이슈 공유
- Conventional Commits (feat:, fix:, docs:) 적용
문서화 및 지식 공유:
- 주간 회의록 작성: 기술 결정사항 (PostgreSQL 배열 타입 선택, 크롤링 전략), 이슈 해결 방법 기록
- Slack 채널로 즉시 공유: 크롤링 에러 해결 방법 (HTTP 502/503 재시도 로직), EC2 배포 가이드
- API 문서화: Swagger로 BlogTemplate API 문서 작성, 프론트엔드 팀과 JSON 응답 형식 협의
- 코드 리뷰 시 결정사항 문서화: PR 코멘트에 "왜 이렇게 구현했는지" 기록
3. 인프라 비용 최적화 및 배포
EC2 인스턴스 선정:
- t2.micro (프리티어) 시도했지만 Spring Boot 실행 시 메모리 부족 (OOM)
- t3.small (2 vCPU, 2GB RAM) 선택, 비용 대비 성능 균형점
배포 및 운영:
- AWS EC2 t3.small 배포 전체 담당 (팀 5명 배포 가이드 작성)
- Docker 이미지 빌드 자동화
- PostgreSQL Docker 컨테이너 구성
- SSH 로그 모니터링, 환경 변수 관리
- Backend: Spring Boot 3.5.7, Java 21
- Database: PostgreSQL 16 (배열 타입으로 colors[], seasons[] 저장)
- AI: Spring AI 1.0.0-M4, OpenAI API
- Crawling: JSoup 1.18.3, RestClient
- Infrastructure: AWS EC2 t3.small, Docker
기술 선택 이유:
- PostgreSQL 16: 배열 타입으로 다중 값 저장 (정규화 없이
colors[]저장하여 JOIN 불필요) - RestClient: RestTemplate 대비 최신 Spring 6.1+ API, 간결한 문법
2025.11 - 2025.12 | 팀 프로젝트 (5명) | 부트캠프 최종 프로젝트
사용자 퀴즈 분석부터 AI 추천, 코디 이미지 생성까지 자동화하는 플랫폼
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Frontend │────────▶│ Spring Boot │────────▶│ FastAPI │
│ (React) │ │ (Java 21) │ │ (Python 3.11) │
└─────────────┘ │ │ │ │
│ - Product │ │ - OpenAI API │
│ - Recommendation│ │ - ChromaDB │
│ - User │ │ - Image Process│
└─────────┬────────┘ └────────┬────────┘
│ │
┌─────────▼────────┐ ┌─────────▼────────┐
│ PostgreSQL 16 │ │ AWS S3 │
│ (pgvector) │ │ (이미지 저장) │
└──────────────────┘ └──────────────────┘
- Product 도메인: 패션 상품 크롤링, 상품 DB 설계 (단독)
- AI 서비스: Python FastAPI 전체 개발 (OpenAI, ChromaDB, 단독)
- Recommendation 통합: Java와 Python 마이크로서비스 통합 (단독)
1. CLAUDE.md 기반 팀 지식 관리 체계 구축
문제 인식:
- 회의에서 결정된 기술적 선택(PostgreSQL 배열 타입, 카테고리 매핑 로직)이 각자 머리에만 있음
- Claude Code에게 질문할 때 팀 최신 결정사항이 반영 안 되어 혼동 발생
- 예: "상품 색상 어떻게 저장?" 질문 시 Claude는 "정규화해서 별도 테이블"이라 답변했지만 팀 결정과 다름
해결 - CLAUDE.md 문서 작성:
내용 구성:
- 프로젝트 구조: 도메인별 역할, 모듈 간 관계
- 코딩 컨벤션: Service는 @Transactional 필수, DTO는 record 사용
- 회의 결정사항 (날짜별):
- "2025.11.20: PostgreSQL 배열 타입 사용 결정 (정규화 대신)"
- "2025.11.22: 무신사 크롤링 시 1초 지연 필요 (봇 탐지 우회)"
- 문서화 요령:
- 언제: 회의 후 즉시, 중요한 기술 선택 시
- 무엇: 기술 선택 이유, 변경사항, 주의사항
- 어떻게: 날짜 명시, 이유 포함, 간결하게
결과:
- Claude Code가 팀 결정사항 이해하고 일관된 코드 생성 (예: "크롤링 코드 작성" 요청 시 자동으로 1초 지연 로직 포함, "상품 색상 저장 로직" 요청 시 PostgreSQL 배열 타입 활용)
- 팀원 간 기술적 결정 공유 및 혼동 방지
- 문서화 습관 정착으로 지속 가능한 협업 체계
2. AI 추천 시스템 (Python FastAPI - 전담)
기술 선택 배경:
FastAPI 선택 이유 (Flask 대신):
- OpenAI API 호출 시 비동기 처리 필수, async/await 네이티브 지원
- Pydantic으로 타입 안전성 확보
- 자동 OpenAPI 문서 생성 (Java 팀과 협업 용이)
ChromaDB 선택 이유 (Pinecone 대신):
- Pinecone 유료 (월 $70), ChromaDB 무료
- Docker로 로컬 운영 가능
- Python 네이티브, OpenAI 임베딩 호환
구현 내용:
@app.post("/api/recommend")
async def recommend(quiz_result: QuizResultRequest):
# ChromaDB 벡터 검색으로 카테고리별 유사 상품 추출
results = collection.query(
query_texts=[quiz_result.style_keyword],
n_results=15,
where={"category": {"$in": ["top", "bottom", "shoes"]}}
)
# 프롬프트: 이상한 조합 방지 조건 명시
response = await client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system",
"content": "상의 1개, 하의 1개, 신발 1개를 선택하고 색상 계열을 통일하세요."},
{"role": "user", "content": str(results["documents"])}
]
)
return {"outfit": response.choices[0].message.content}- OpenAI API (GPT-3.5-turbo)로 퀴즈 답변 분석
- ChromaDB 벡터 검색으로 카테고리별 유사 상품 추출
- rembg (U2-Net)로 상품 이미지 배경 제거
- Pillow로 코디 합성 이미지 생성
- AWS S3에 이미지 업로드 (boto3)
프롬프트 엔지니어링:
- 초기: "코디 추천해줘" 요청 시 이상한 조합 (상의 3개, 하의 0개)
- 개선: "상의 1개, 하의 1개, 신발 1개, 색상 계열 통일" 명시
- 결과: 추천 품질 향상, 자연스러운 코디 조합
테스트 코드 (pytest):
- OpenAI API 호출 모킹 테스트
- ChromaDB 검색 결과 검증
- 이미지 처리 파이프라인 단위 테스트
3. 마이크로서비스 통합 (Java와 Python)
구현:
// Java → Python FastAPI 호출 (타임아웃 + 중복 조합 방지)
private final Set<Long> previousIds = new TreeSet<>();
public RecommendResult callAiService(QuizResult quizResult) {
RecommendResult result = restClient.post()
.uri(AI_SERVICE_URL + "/api/recommend")
.contentType(MediaType.APPLICATION_JSON)
.body(quizResult)
.retrieve()
.body(RecommendResult.class);
// 이전 추천과 중복 시 재요청
if (previousIds.containsAll(result.getProductIds())) {
return callAiService(quizResult);
}
previousIds.addAll(result.getProductIds());
return result;
}- Java RestClient로 Python FastAPI 호출
- 중복 조합 회피: TreeSet으로 이전 추천 ID 추적
- 타임아웃 설정 (연결 5초, 읽기 60초)
협업 및 문서화:
API 문서 기반 협업:
- Swagger 문서로 Java/Python 마이크로서비스 API 스펙 정의
- 프론트엔드 팀과 JSON 응답 구조 협의 (필드명, 타입, 예시 값)
- API 변경 시 Swagger 문서 즉시 업데이트 → Slack 공지 → 팀원 확인 프로세스
CLAUDE.md 기반 지식 공유:
- 기술 결정사항 날짜별 기록 (PostgreSQL 배열 타입, 카테고리 매핑 로직)
- 크롤링 로직 주의사항 문서화 (1초 지연, User-Agent 설정)
- 신규 팀원이 CLAUDE.md 읽고 즉시 개발 시작 가능한 수준으로 작성
코드 리뷰 및 품질 관리:
- GitHub PR 리뷰, CodeRabbit AI 리뷰 적극 활용
- Conventional Commits 적용 (feat:, fix:, docs:)
- AWS EC2 배포 전체 담당 (팀원 배포 가이드 작성)
- Docker Compose로 Java/Python 마이크로서비스 통합 배포
- PostgreSQL Docker 컨테이너 구성
- 환경 변수 관리 (.env), 멀티 컨테이너 오케스트레이션
- AWS S3 이미지 저장소 구성 및 권한 관리
- SSH 로그 모니터링
- Backend: Spring Boot 3.2.0, Java 21, PostgreSQL 16 (pgvector)
- AI Service: FastAPI 0.109.0, Python 3.11, OpenAI API, ChromaDB
- Image: Pillow, rembg (U2-Net)
- Infrastructure: AWS EC2, Docker Compose, AWS S3
- Frontend: React 19, TypeScript 5.9, Vercel
- Testing: JUnit 5, Mockito, pytest
2026.03 (1주) | 개인 프로젝트
소프트랩스 Java 크롤링 경험을 Node.js로 재구현 (FETCHING 지원 대비)
소프트랩스 인턴에서 Java로 패션 크롤링을 경험한 후, Node.js 생태계 학습을 위해 같은 도메인(무신사 크롤링)을 NestJS로 재구현한 프로젝트.
목표: Java와 Node.js의 비동기 처리 방식 차이를 실전으로 학습
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Crawler │────────▶│ NestJS │────────▶│ MySQL │
│ (Axios) │ │ (TypeScript) │ │ (TypeORM) │
└─────────────┘ │ │ │ │
│ - Products API │ │ - products │
│ - Crawler API │ │ - price_history│
└──────────────────┘ └─────────────────┘
1. 무신사 API 리버스 엔지니어링
문제 인식:
- 초기: Puppeteer 헤드리스 브라우저로 HTML 파싱 시도
- 실패: 무신사가 SPA 구조로 변경되어 동적 렌더링 파싱 실패
- 느리고 복잡한 브라우저 자동화
해결 - 무신사 내부 API 직접 호출:
리버스 엔지니어링 과정:
- Chrome DevTools Network 탭에서 무신사 검색 페이지 분석
api.musinsa.com도메인의 JSON API 발견- 요청 헤더 분석 (User-Agent, Accept) 후 Postman 테스트
- 나이키 신발 카테고리 API 엔드포인트 확인
- Axios로 구현 (Puppeteer 대비 10배 빠름)
// 소프트랩스에서 배운 방식: 내부 API 직접 호출
const apiUrl = 'https://api.musinsa.com/api2/dp/v2/plp/goods?brand=nike&category=103';
const response = await axios.get(apiUrl);
const products = response.data.data.list; // 30개 상품결과:
- 30개 나이키 신발 실시간 수집 (에어맥스, 에어조던 등)
- Puppeteer 대비 응답 속도 10배 향상
- 안정적인 JSON 파싱
2. 가격 변동 추적 시스템 설계
문제:
- 매번 크롤링할 때마다 히스토리를 저장하면 불필요한 데이터 증가
- 가격이 변하지 않았는데도 중복 저장됨
해결 - 효율적인 히스토리 관리:
DB 설계:
products: 상품 기본 정보 (goods_no UNIQUE로 중복 방지)price_history: 가격 변동 시에만 저장
가격 변동 감지 로직:
// goods_no로 기존 상품 조회
// 신규 상품: 생성 + 히스토리 저장
// 기존 상품: 가격/품절 상태 변경 시에만 히스토리 저장
if (product.price !== productData.price) {
await this.createHistory(product);
}실제 성과:
- 아스트로그래버: 139,000원 → 125,100원 (10% 할인 감지)
- 불필요한 데이터 저장 방지 (변동 없으면 히스토리 미생성)
- 할인가/정상가 구분 추적
3. Java vs Node.js 비동기 처리 비교 학습
소프트랩스 Java 방식:
- 블로킹 I/O: 응답 대기 중 스레드 block
- 요청당 스레드 생성, 블로킹으로 메모리 낭비
Node.js 방식:
- 논블로킹 I/O: 이벤트 루프가 다른 작업 처리
- 단일 스레드 이벤트 루프, 논블로킹으로 효율적
- async/await으로 비동기 코드를 동기처럼 작성
| 항목 | Java (소프트랩스) | Node.js (이 프로젝트) |
|---|---|---|
| HTTP | RestClient | Axios |
| 파싱 | JSoup | 무신사 API 직접 호출 |
| I/O | 블로킹 | 논블로킹 (async/await) |
| 프레임워크 | Spring Boot | NestJS |
| ORM | JPA | TypeORM |
학습 포인트:
- Java: 요청당 스레드 생성, 블로킹으로 메모리 낭비
- Node.js: 단일 스레드 이벤트 루프, 논블로킹으로 효율적
- async/await으로 비동기 코드를 동기처럼 작성
4. TypeORM 관계 설정
- Product ↔ PriceHistory 양방향 관계 구현
- @OneToMany, @ManyToOne 데코레이터로 자동 JOIN
- GET /products/:id/history: 상품별 가격 변동 히스토리 조회
- Swagger로 자동 문서화
- 30개 나이키 신발 실시간 추적
- goods_no UNIQUE constraint로 중복 방지
- 가격 변동 시에만 히스토리 저장 (효율적 DB 설계)
- Java RestClient → Node.js Axios 전환 경험
- Puppeteer 실패 → API 직접 호출 성공
- Backend: NestJS, TypeScript, TypeORM
- Database: MySQL 8.0
- Crawler: Axios (무신사 API 직접 호출)
- Infrastructure: Docker Compose
- Documentation: Swagger UI
- Backend: 800+ 라인 (TypeScript)
- REST API: 6개 엔드포인트
- 30개 상품 추적, 가격 히스토리 자동 관리
2026.03 (2주) | 개인 프로젝트
Reactive Programming 학습 및 면접 대비를 위한 개발 용어 퀴즈 플랫폼
면접 준비를 위한 개발 용어 학습 플랫폼. Spring WebFlux + R2DBC로 Reactive Programming 실습.
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Next.js │────────▶│ Spring WebFlux │────────▶│ PostgreSQL │
│ (TypeScript)│ │ (Kotlin) │ │ (R2DBC) │
└─────────────┘ │ │ │ │
│ - REST API │ │ - 6 카테고리 │
│ - Reactive │ │ - 42 문제 │
└─────────┬────────┘ └─────────────────┘
│
┌─────────▼────────┐
│ Redis │
│ (캐싱) │
└──────────────────┘
1. Reactive Programming 구현
기술 선택 배경:
Spring WebFlux 선택 이유 (Spring MVC 대신):
- 비동기 논블로킹 I/O로 높은 동시성 처리
- Reactive Streams 표준 준수 (Mono, Flux)
- 메모리 효율적 백프레셔 관리
R2DBC 선택 이유 (JDBC 대신):
- Reactive Database 연동 (PostgreSQL)
- 비동기 쿼리 실행
- WebFlux와 완벽한 통합
구현 내용:
@GetMapping("/api/quizzes")
fun getAllQuizzes(): Flux<QuizResponse> {
return quizRepository.findAll()
.map { it.toResponse() }
.limitRate(10) // 백프레셔 관리
}- Spring WebFlux 비동기 논블로킹 REST API 6개 구현
- R2DBC로 PostgreSQL Reactive 연동
- Mono/Flux 기반 스트림 처리와 백프레셔 관리
- Redis 캐싱 (퀴즈 데이터)으로 응답 속도 개선
결과:
- 동시성 처리 향상 (메모리 효율적)
- 비동기 논블로킹으로 응답 속도 개선
2. TypeScript + Kotlin 타입 안정성
기술 조합 이유:
- Kotlin: null-safety, data class, sealed class로 타입 안전성
- TypeScript: 프론트엔드 타입 안전성, API 응답 타입 정의
- 양방향 타입 체크로 런타임 에러 최소화
구현:
// TypeScript
interface QuizResponse {
id: number;
question: string;
options: string[];
correctAnswer: string;
category: string;
}// Kotlin
data class QuizResponse(
val id: Long,
val question: String,
val options: List<String>,
val correctAnswer: String,
val category: String
)- Next.js 15 App Router 동적 라우팅
- TailwindCSS 반응형 UI
- API 응답 타입 정의 (TypeScript interface ↔ Kotlin data class 매핑)
결과:
- 양방향 타입 체크로 런타임 에러 최소화
- API 변경 시 컴파일 타임 에러로 조기 발견
3. 테스트 주도 개발
테스트 전략:
- JUnit 5 + MockK: 단위 테스트 10개
- WebFlux 테스트: WebTestClient로 비동기 API 테스트
- 통합 테스트: TestContainer PostgreSQL
구현:
@Test
fun `퀴즈 조회 API 테스트`() {
webTestClient.get()
.uri("/api/quizzes/1")
.exchange()
.expectStatus().isOk
.expectBody<QuizResponse>()
.consumeWith { response ->
assertThat(response.responseBody?.id).isEqualTo(1)
}
}// StepVerifier로 Reactive 스트림 검증
@Test
fun `카테고리 필터링 테스트`() {
every { quizRepository.findByCategory("Java") } returns
Flux.just(quiz1, quiz2)
StepVerifier.create(quizService.getByCategory("Java"))
.expectNextMatches { it.category == "Java" }
.expectNextMatches { it.category == "Java" }
.verifyComplete()
}
// WebTestClient로 비동기 API 통합 테스트
@Test
fun `존재하지 않는 퀴즈 조회 시 404 반환`() {
webTestClient.get()
.uri("/api/quizzes/999")
.exchange()
.expectStatus().isNotFound
}- 퀴즈 CRUD API 테스트 (7개 테스트 메서드)
- 카테고리 필터링 테스트
- 예외 처리 테스트
- Reactive 비동기 테스트 (StepVerifier로 Mono/Flux 검증)
- Repository 모킹 및 Service 계층 단위 테스트
테스트 커버리지:
- 7개 테스트 메서드 (QuestionServiceTest)
- JUnit 5 + MockK 단위 테스트
- Reactive 비동기 테스트 (StepVerifier)
- WebTestClient로 비동기 API 통합 테스트
4. Next.js 15 App Router
app/
├── page.tsx (메인 페이지)
├── quiz/
│ └── [category]/
│ └── page.tsx (카테고리별 퀴즈)
└── layout.tsx (공통 레이아웃)
특징:
- Server Components 기본 지원 (성능 향상)
- 동적 라우팅 간소화
- TailwindCSS 반응형 디자인
- Reactive Programming으로 높은 동시성 처리
- TypeScript + Kotlin 타입 안정성 확보
- 테스트 커버리지로 코드 품질 보장
- Swagger UI 자동 API 문서화
- Backend: Kotlin, Spring Boot 3.2, WebFlux, PostgreSQL (R2DBC), Redis
- Frontend: Next.js 15, TypeScript, TailwindCSS
- Testing: JUnit 5, MockK (10개 테스트)
- Infrastructure: Docker Compose
- Documentation: Swagger UI
- Backend: 1,700+ 라인 (Kotlin)
- Frontend: 800+ 라인 (TypeScript)
- 총 6개 REST API, 42개 샘플 데이터
2025.09 - 2025.10 | 팀 프로젝트 (6명)
Steam API 연동 게임 정보 수집 + 소셜 로그인 + 결제 시스템을 갖춘 종합 게임 판매 플랫폼
Steam API 연동 게임 정보 수집 + 소셜 로그인 + 결제 시스템
User, Security 모듈 전체 담당 (단독)
1. JWT + OAuth2 인증 시스템 구현
기술 선택 배경:
- JWT Stateless 선택 (Session 대신): 수평 확장 용이, 서버 간 세션 공유 불필요, Redis 인프라 비용 절감
- HttpOnly 쿠키 선택 (LocalStorage 대신): XSS 공격 방어, Access Token (6시간) 보안 강화
구현 내용:
- Google, Kakao, Naver OAuth2 통합 (provider별 attribute 매핑)
- 커스텀 필터로 Authorization 헤더 + HttpOnly 쿠키 동시 인증
- 신규 유저 자동 가입 (upsert)
// Authorization 헤더 + HttpOnly 쿠키 동시 인증 필터
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String token = extractToken(request);
if (token != null && jwtProvider.validateToken(token)) {
Authentication auth = jwtProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
// Authorization 헤더 우선, 없으면 HttpOnly 쿠키에서 추출
String header = request.getHeader("Authorization");
if (header != null) return header.replace("Bearer ", "");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
return Arrays.stream(cookies)
.filter(c -> "accessToken".equals(c.getName()))
.map(Cookie::getValue)
.findFirst().orElse(null);
}
return null;
}
}문제 해결 - OAuth2 테스트 어려움:
- 문제: OAuth2는 로컬 테스트 불가 (실제 provider 서버 필요)
- 발생 이슈: 백엔드 인증 후 프론트엔드 리다이렉트 실패, 각 provider(Google, Kakao, Naver)마다 리다이렉트 URL 형식 상이
- 해결: OAuth2 성공 핸들러에서 프론트 URL로 JWT 포함 리다이렉트, 각 provider 콘솔에서 정확한 리다이렉트 URL 등록
- 결과: 실행하며 문제 발견 → 즉시 수정 반복으로 안정적인 소셜 로그인 구현
2. 동시성 환경 팔로우 중복 방지 이중 방어
문제 인식:
- 같은 사용자를 동시에 여러 번 팔로우하면 중복 insert 가능성
- 서비스 로직만으로는 동시 요청 시 중복 체크 통과
해결 전략:
- DB 레벨 방어: @UniqueConstraint (user_id, target_type, target_id)로 데이터 무결성 보장
- 서비스 레벨 방어: 선행 검증으로 사용자 친화적 에러 메시지 반환
- 중복 팔로우: "이미 팔로우 한 상태입니다"
- 자가 팔로우: "자기 자신을 팔로우할 수 없습니다"
// DB 제약
@UniqueConstraint(columnNames = {"user_id", "target_type", "target_id"})
// 서비스 로직
if (followRepository.existsByUserIdAndTargetTypeAndTargetId(...)) {
throw new IllegalArgumentException("이미 팔로우 한 상태입니다");
}결과:
- 동시 요청 상황에서도 중복 팔로우 완벽 차단
- ConstraintViolationException 대신 명확한 에러 안내로 UX 개선
- 29개 테스트 파일 (Service, Controller, Repository 계층)
- OAuth2 인증 통합 테스트 (Google, Kakao, Naver)
- 팔로우 서비스 단위 테스트
- 중복 팔로우, 자가 팔로우 예외 케이스 검증
- 주문/결제 통합 테스트
- Backend: Spring Boot 3.5.5, Java 21
- Database: MySQL 8.0
- Security: JWT, OAuth2 (Google, Kakao, Naver)
- Testing: JUnit 5, Mockito (29개 테스트 파일)
2025.08 - 2025.10 | 팀 프로젝트 (3명)
Yes24 스타일 온라인 서점 플랫폼
팀 리더 + Order 모듈 (단독)
1. 팀 리더십 및 프로젝트 기획
프로젝트 구조 설계:
- User, Book, Order 도메인 분담 결정
- 기술 스택 선정 및 근거 설명 (Spring Boot, MySQL, Redis, KakaoPay API)
협업 관리:
- 주간 회의 주도, 일정 관리, 이슈 트래킹
- Git 브랜치 전략 수립 (feature → develop → main)
- 코드 리뷰 프로세스 운영
2. Order 모듈 개발
주문-재고 트랜잭션 처리:
구현:
@Transactional
public Order createOrder(OrderRequest request) {
// 1. 재고 확인 (부족 시 즉시 실패)
if (book.getStock() < request.getQuantity()) {
throw new OutOfStockException();
}
// 2. 주문 생성 + 재고 차감
Order order = orderRepository.save(new Order(...));
book.decreaseStock(request.getQuantity());
// 3. 결제 처리
paymentService.pay(order);
// 성공: 커밋, 실패: 롤백 (재고 복원)
return order;
}- 장바구니 기반 주문 생성 및 재고 확인
- @Transactional로 주문-재고 원자성 보장
- 재고 부족 시 주문 생성 전 예외 발생
- 결제 실패 시 자동 롤백 (재고 복원)
비즈니스 로직:
- 재고 확인하여 부족 시 즉시 실패
- 주문 생성하고 재고 차감
- 결제 처리
- 성공하면 커밋, 실패하면 롤백
결과: 동시성 문제 없이 데이터 일관성 유지
- OrderServiceTest: 15개 테스트 메서드
- ArgumentCaptor로 메서드 호출 검증
- 트랜잭션 통합 테스트 (주문-결제-재고 연계)
- 결제 실패 시 롤백 검증
- 재고 부족, 빈 장바구니 등 edge case 커버
- Spring Boot 3.5, Java 21, MySQL 8.0, Redis 7, KakaoPay API
- Testing: JUnit 5, Mockito
2024.09 - 2024.12 | 팀 프로젝트 (5명)
공연 정보 조회, 좌석 선택, 예매, 커뮤니티 기능
팀 리더 + Frontend Developer + Controller
1. 팀 리더십
- 프로젝트 도메인 분담 (Performance, Reservation, User, Community)
- 기술 스택 선정 (Spring Boot 3.2.4, Java 17, Thymeleaf, H2)
- Git 브랜치 전략 및 협업 프로세스 정립
2. 전역 UI/UX 체계 구축
- 전역 헤더, 푸터, 네비게이션 바 설계
- 공통 CSS/JS 컴포넌트화로 화면 일관성 확보
- 반응형 디자인 적용
3. 공연 예매 시스템 UI
- JavaScript 기반 동적 좌석 선택
- 실시간 예약 상태 표시 (예약 가능/선택됨/예약 완료)
- AJAX 비동기 처리 (댓글 작성/수정/삭제)
4. Spring Security 연동 UI
- Thymeleaf로 로그인/OAuth 버튼 구현
- CSRF 토큰 hidden input 처리
- Security 태그로 권한별 메뉴 동적 표시/숨김
- Spring Boot 3.2.4, Java 17, Thymeleaf, H2, Spring Security
- Frontend: HTML5, CSS3, JavaScript ES6
2025.10 - 2025.11 | 팀 프로젝트 (4명)
LangChain 기반 AI 대출 상담 챗봇
팀 리더 + Compute Policies 모듈
사용자 질문 → ChromaDB 검색 → LLM 응답 생성 → 금융 계산 엔진 → 대출 가능 여부 판단
1. 팀 리더십 및 문서화
- 금융 계산 엔진 구조 설계 (LTV, DTI, DSR)
- README 작성: 프로젝트 개요, 실행 방법, 환경 변수 설정 가이드
- 시스템 흐름도, API 문서, 테스트 케이스 문서화
- Slack 주간 회고: 완료 작업, 다음 주 계획, 이슈 해결 방법 공유
2. Compute Policies 모듈
- LTV, DTI, DSR 계산 로직
- 대출 가능 여부 판단
- 정책 파라미터 버전 관리
3. 프롬프트 엔지니어링 - 챗봇 답변 품질 개선
- 문제: GPT-3.5-turbo 사용 초기, 대출 가능 여부 질문에 금융 계산 근거 없이 단정적으로 답변하거나 ChromaDB에서 불러온 금융 정보를 잘못 해석하는 경우 발생
- 해결: GPT-3.5-turbo → GPT-4o로 모델 교체 (복잡한 금융 조건 분기 처리 향상)
- 시스템 프롬프트 개선: 금융 상담사 역할 명시, LTV/DTI/DSR 계산 근거를 응답에 포함하도록 명시, ChromaDB 검색 문서 기반으로만 답변하도록 제약 추가
# GPT-4o + ChromaDB RAG 체인
SYSTEM_PROMPT = """당신은 전문 대출 상담사입니다.
1. LTV, DTI, DSR 계산 근거를 수치와 함께 명시하세요.
2. 제공된 문서(context)에 없는 내용은 '확인이 필요합니다'로 표시하세요.
3. 불확실한 정보를 단정적으로 답변하지 마세요."""
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| ChatPromptTemplate.from_messages([
("system", SYSTEM_PROMPT),
("human", "{question}\n\n참고 문서:\n{context}")
])
| ChatOpenAI(model="gpt-4o", temperature=0)
| StrOutputParser()
)- 결과: 계산 근거가 포함된 구조화된 응답 생성, 불확실한 정보 단정 답변 감소
- Python 3.11, LangChain, LangGraph, ChromaDB, FastAPI, OpenAI GPT-3.5-turbo, GPT-4o
- Java 21, Spring Boot 3.x: JPA, Security, RESTful API
- Kotlin, Spring Boot 3.2: WebFlux (Reactive Programming), R2DBC
- Python 3.11, FastAPI: 비동기 웹 프레임워크
- Next.js 15: App Router, Server Components
- TypeScript, TailwindCSS
- React 19
- PostgreSQL 16: 배열 타입, JSONB, pgvector, R2DBC (Reactive)
- MySQL 8.0: 인덱스 최적화, 트랜잭션 관리
- Redis 7: 캐싱
- OpenAI API: GPT-3.5-turbo, GPT-4o, 프롬프트 엔지니어링
- ChromaDB: 벡터 검색
- LangChain/LangGraph: RAG 파이프라인
- JSoup: HTML 파싱
- RestClient (Spring 6.1+): REST API 크롤링
- JUnit 5: Mockito (Java), MockK (Kotlin)
- pytest: Python 단위 테스트
- WebTestClient: WebFlux 비동기 테스트
- AWS: EC2 (t3.small)
- Docker: 멀티 스테이지 빌드, Docker Compose
- Git/GitHub: Conventional Commits (feat:, fix:, docs:)
- CodeRabbit: AI 코드 리뷰
- Swagger: API 문서화
- Slack: 팀 커뮤니케이션
팀 협업 최적화
- CLAUDE.md 기반 팀 지식 관리: 회의 결정사항 문서화, Claude Code가 팀 맥락 이해
개발 생산성 도구
- Claude Code (Sonnet 4.5): 크롤링 로직, 테스트 코드 작성
- GitHub Copilot: DTO, Entity 자동 완성
- ChatGPT: 기술 스택 비교, 에러 디버깅
AI 프로젝트 경험
- OpenAI API 프롬프트 엔지니어링
- CodeRabbit AI 코드 리뷰
패스트캠퍼스 AI융합 백엔드 개발 부트캠프 2025.06 - 2025.12 | 서울 강남
- 6개월 풀타임 (Java, Spring Boot, Python, AI)
- 5개 팀 프로젝트, Git 협업 경험
- SQLD (SQL 개발자) (2025)
- 정보처리기사 (2025)
- TOEIC 865점 (2025)
- 영문 기술 문서 독해 가능
- Email: jaehun1417@gmail.com
- Phone: 010-8801-9685
- GitHub: https://github.com/doublejh0501
- Location: 경기도 남양주시
최종 업데이트: 2026.03.30




