| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- Android
- playwright
- 도메인설계
- typescript
- 보안
- 상태관리
- python
- FastAPI
- 인증
- React
- Vite
- 크롤링
- electron
- 배포
- javascript
- next.js
- contextBridge
- Kotlin
- sqlite
- 아키텍처
- 마이그레이션
- 프론트엔드
- 소셜로그인
- REST API
- JWT
- github actions
- php
- 백엔드
- 온디바이스AI
- 메일자동화
- Today
- Total
HM소프트
이력서·자기소개서 엔티티 설계와 파일 업로드 — 채용 플랫폼 (2) 본문
느리고 불안정한 LLM 호출을 별도 마이크로서비스로 격리해 핵심 기능을 보호하고, 키·시크릿 이중 인증으로 잠근 설계를 다룬다. 전역 설정 프롬프트로 LLM을 결정론적 함수로 다루고 temperature 0과 JSON 스키마로 출력을 강제하며, 이력서 피드백·초안 생성·커뮤니티 질문에서 환각을 프롬프트로 막은 채용 플랫폼 기록이다.
이력서·자기소개서 엔티티 설계와 파일 업로드 구현을 정리한다.

2편: 이력서 도메인
1편에서 인증을 세웠다. 인증된 사용자에 묶이는 핵심 데이터가 이력서·자기소개서·경력기술서다. 이번 편은 이 도메인의 엔티티 설계와 파일 업로드, 그리고 마지막에 만난 스키마 정렬 작업이다.
도메인 나누기: 이력서 ≠ 자소서 ≠ 경력기술서
처음에 "이력서 하나"로 뭉뚱그릴 뻔했지만, 셋은 성격이 다르다.
- 이력서(Resume): 인적사항·학력·경력의 구조화된 데이터. 목록으로 관리(여러 버전).
- 자기소개서(Cover Letter): 자유 서술형 텍스트 중심.
- 경력기술서(Career Description): 경력을 상세 기술하는 구조화 문서.
각각을 별도 엔티티로 나누고, 사용자에 연결했다. 한 사용자가 여러 이력서를 가질 수 있으니 목록(list) 기능이 필요했다.
@Entity()
export class Resume {
@PrimaryGeneratedColumn() id: number;
@ManyToOne(() => User, (u) => u.resumes)
user: User; // 1편의 인증 사용자에 묶임
@Column() title: string;
@OneToMany(() => Career, (c) => c.resume, { cascade: true })
careers: Career[]; // 경력 항목들
@Column({ nullable: true }) fileUrl: string; // 첨부 파일
@CreateDateColumn() createdAt: Date;
}
기능을 단계적으로 쌓았다: 이력서 작성 → 이력서 목록 → 경력기술서 작성 → 자기소개서 작성. 각 단계가 엔티티 하나와 그에 대한 CRUD로 떨어졌다.
파일 업로드
이력서는 텍스트 데이터만이 아니라 파일 첨부(PDF 이력서 등)도 받는다. 업로드된 파일을 저장하고, 엔티티에는 그 경로/URL을 들고 있게 했다. 파일 자체와 메타데이터(엔티티)를 분리해, DB에는 참조만 두고 파일은 스토리지에 두는 일반적인 패턴을 따랐다.
업로드에서 신경 쓴 것:
- 소유권: 업로드한 파일은 그 사용자의 이력서에만 연결. 남의 파일에 접근 못 하게(1편의 인증/가드와 연결).
- 파일 메타: 원본 파일명·타입·크기를 엔티티에 기록.
마지막 관문: 엔티티-DB 스키마 정렬
기능을 다 붙인 뒤 가장 큰 작업이 "엔티티 정의와 실제 DB 스키마를 정확히 일치시키는" 것이었다. 개발 중 엔티티를 여러 번 고치다 보면, 코드상의 엔티티와 실제 DB 테이블이 미묘하게 어긋난다(컬럼 타입, nullable, 관계, 인덱스).
이게 어긋나면 런타임에서 "컬럼이 없다", "타입이 안 맞는다" 류의 에러가 터진다. 엔티티-DB 스키마를 전면 정렬하고, 백엔드 전체를 통합하는 작업으로 시리즈의 백엔드 토대를 마무리했다.
> 이건 다른 글에서 다룬 Room(안드로이드) 스키마 정렬과 같은 교훈이다 — ORM/마이그레이션 도구는 "코드의 모델"과 "DB의 실제"가 일치한다고 가정하므로, 그 일치를 깨면 조용히 혹은 시끄럽게 터진다.
정리하며
- 도메인을 성격대로 분리 — 이력서/자소서/경력기술서를 별도 엔티티로.
- 파일과 메타데이터 분리 — DB엔 참조, 파일은 스토리지. 소유권은 인증에 묶음.
- 엔티티-DB 스키마 정렬 — 개발 중 어긋난 모델과 실제 DB를 일치시키는 게 통합의 핵심.
다음 3편에서는 이 백엔드를 소비하는 프론트엔드(Next.js 15 + React 19)와 지도 UI를 다룬다.
'외주 개발일지' 카테고리의 다른 글
| Next.js 15 + React 19 프론트엔드와 지도 UI — 채용 플랫폼 (3) (0) | 2026.06.12 |
|---|---|
| LLM 오케스트레이터로 이력서 피드백·초안 생성 — 채용 플랫폼 (4) (0) | 2026.06.12 |
| 채용 플랫폼 백엔드 인증 구현 — 기본 인증 + SNS 소셜 로그인 (1) (0) | 2026.06.12 |
| Electron 키오스크 모드와 웹 데모 배포 — 현장 키오스크 개발기 (4) (0) | 2026.06.11 |
| Electron 하드웨어 연동 개발기 (3) — 스텁 드라이버와 IPC 상태 통합 (0) | 2026.06.11 |