🧪 실전 예제로 배우는 Git Flow 전략
- 기능 개발부터 릴리스, 버그 수정까지 한눈에 흐름 정리


📘 시나리오

지금부터 MyShop이라는 쇼핑몰 프로젝트에서 로그인 기능 개발 → 버전 릴리스 → 긴급 버그 수정이라는 흐름을 Git Flow 전략에 따라 진행해보겠습니다.


1️⃣ 프로젝트 초기화

# 기본 브랜치(main)는 이미 존재한다고 가정
git checkout -b develop
git push -u origin develop
  • main은 항상 배포 가능한 안정 코드
  • develop은 다음 릴리스를 위한 통합 개발 브랜치

2️⃣ 기능 개발 (로그인 기능)

# develop에서 새로운 기능 브랜치 생성
git checkout develop
git checkout -b feature/login

# 로그인 기능 개발 작업
# 파일 수정, 커밋...
git add .
git commit -m "feat: 로그인 기능 구현"

# 기능 완료 후 develop에 병합
git checkout develop
git merge feature/login
git branch -d feature/login

🔄 정리

  • feature/login 브랜치에서 기능을 개발하고
  • 완료되면 develop으로 병합 후 삭제

3️⃣ 릴리스 준비 (v1.0.0)

# release 브랜치 생성
git checkout develop
git checkout -b release/1.0.0

# 테스트 중 발견된 마이너 이슈 수정
git commit -am "fix: 로그인 버튼 색상 조정"

# 릴리스 완료 → main에 병합
git checkout main
git merge release/1.0.0
git tag -a v1.0.0 -m "🎉 Release v1.0.0"

# develop에도 병합 (테스트 수정사항 반영)
git checkout develop
git merge release/1.0.0

# release 브랜치 삭제
git branch -d release/1.0.0

✅ main은 최신 배포 버전
✅ develop은 다음 기능 개발 준비 완료


4️⃣ 긴급 버그 수정 (v1.0.1)

배포 후 로그인 시 예외 발생 보고됨! 🔥

# main에서 hotfix 브랜치 생성
git checkout main
git checkout -b hotfix/login-crash

# 문제 수정
git commit -am "fix: 로그인 시 NullPointerException 발생 문제 해결"

# 수정 완료 → main에 병합
git checkout main
git merge hotfix/login-crash
git tag -a v1.0.1 -m "🔧 Hotfix: 로그인 버그 수정"

# develop에도 병합 (다음 개발에 반영)
git checkout develop
git merge hotfix/login-crash

# hotfix 브랜치 삭제
git branch -d hotfix/login-crash

🔄 전체 흐름 요약

main
 └─ release/1.0.0 → 🔀 merge → main + develop (릴리스)
 └─ hotfix/login-crash → 🔀 merge → main + develop (버그 수정)

develop
 └─ feature/login → 🔀 merge → develop (기능 개발)

📌 브랜치별 용도 다시 보기

브랜치 용도
main 운영 중인 최종 코드 (배포 버전)
develop 다음 릴리스를 위한 통합 개발 브랜치
feature/* 기능 단위 개발 (개발자 개인 작업 공간)
release/* 배포 전 테스트/수정/버전 태깅
hotfix/* 운영 중 긴급 수정 및 패치

🧠 팁: Git Flow는 언제 쓰면 좋을까?

  • ✅ 협업 인원이 3명 이상이고
  • ✅ 기능/릴리스/버그 수정이 병렬적으로 발생하며
  • ✅ 릴리스 주기가 명확히 존재할 때

이런 상황이라면 Git Flow는 버전 관리의 복잡성을 줄이고, 팀 간 충돌을 줄여줍니다.


✅ 마무리

Git Flow는 단순히 브랜치를 나누는 것이 아니라,
기능 개발, 테스트, 릴리스, 유지보수 작업의 전체 흐름을 시각적으로 설계하게 해줍니다.

실제 프로젝트에서도 이번 예시처럼

  • 기능 단위로 feature/* 브랜치를 만들고
  • 안정성 테스트를 release/*에서 진행하며
  • 문제가 생기면 hotfix/*로 바로 대응하는 방식이
    협업의 효율성과 서비스 안정성을 크게 높여줍니다.

 

 

'Git > GitHub' 카테고리의 다른 글

Git Branch 전략 - Git Flow  (0) 2025.05.03
GitHub - Image 올리기 (README, Issue, PR)  (0) 2021.08.22

🚦Git Flow 전략 완벽 정리
- 협업을 위한 체계적인 브랜치 관리 전략


✅ Git Flow란?

Git Flow는 Vincent Driessen이 제안한 Git 브랜치 관리 전략으로,
여러 명이 협업할 때 개발, 배포, 수정 작업을 안정적으로 나누어 진행할 수 있도록 체계를 잡아주는 방법입니다.

기본적으로 역할이 명확한 브랜치를 정의하고,
기능 개발 → 테스트 → 배포로 이어지는 일련의 과정을 브랜치 흐름으로 시각화합니다.


🏷️ Git Flow의 5가지 주요 브랜치

브랜치 역할
main 최종 배포된 제품 버전이 존재 (항상 안정적인 코드)
develop 다음 버전을 위한 통합 개발 브랜치, 기능들이 이곳에 모임
feature/* 새로운 기능 개발용 브랜치 (feature/login 등)
release/* 배포 전 단계에서 테스트/버그 수정 진행 (release/1.0.0)
hotfix/* 배포된 main 브랜치에 치명적 버그가 발생했을 때 긴급 수정용 (hotfix/urgent-bug)

🧭 Git Flow 작업 흐름

1. main → 가장 안정적인 배포 코드
2. develop → 기능들을 병합해가는 통합 브랜치

[기능 개발 단계]
- develop → feature/* → develop

[배포 준비 단계]
- develop → release/* → main + develop

[긴급 버그 수정]
- main → hotfix/* → main + develop

🧪 예시 흐름 보기

✅ 기능 개발

git checkout develop
git checkout -b feature/login
# 작업 후
git commit -m "Add login feature"
git checkout develop
git merge feature/login
git branch -d feature/login

✅ 릴리즈 준비

git checkout develop
git checkout -b release/1.0.0
# 테스트 및 버그 수정
git commit -m "Fix minor issues"
git checkout main
git merge release/1.0.0
git tag -a v1.0.0 -m "Release 1.0.0"
git checkout develop
git merge release/1.0.0
git branch -d release/1.0.0

✅ 긴급 수정

git checkout main
git checkout -b hotfix/urgent-fix
# 버그 수정
git commit -m "Fix production bug"
git checkout main
git merge hotfix/urgent-fix
git tag -a v1.0.1 -m "Hotfix release"
git checkout develop
git merge hotfix/urgent-fix
git branch -d hotfix/urgent-fix

📦 Git Flow 전략의 장점

항목 설명
✅ 역할 구분 명확 브랜치마다 목적이 분리되어 협업에 유리
✅ 배포 시점 관리 쉬움 release, hotfix로 안정된 배포 프로세스 가능
✅ 충돌 관리 용이 기능 별 feature 브랜치로 분산 작업 가능
✅ 안정성 확보 main은 항상 배포 가능한 상태로 유지됨

⚠️ 단점 및 실무에서의 보완점

항목 설명
❗ 브랜치가 많아짐 브랜치 관리가 복잡해지고 무거워질 수 있음
❗ 빠른 배포와는 거리 있음 CI/CD 파이프라인과 속도가 안 맞을 수 있음
❗ 릴리즈 주기가 짧으면 비효율 release 브랜치가 너무 자주 생기면 오히려 불편함

💡 실무 팁

  • 1~3인 규모의 사이드 프로젝트에는 단순한 main + feature/*만 써도 충분함
  • 단순화된 GitHub Flow 또는 Trunk Based Development와 혼용하기도 함
  • 릴리즈 자동화(CI/CD)와 함께 쓰려면 release 브랜치를 과감히 생략하기도 함

🔚 마무리

Git Flow는 역할이 명확한 브랜치 전략으로,
개발/테스트/배포 과정을 체계적으로 관리할 수 있게 도와주는 훌륭한 도구입니다.

협업이 많아지고 릴리즈 주기가 명확한 팀이라면,
Git Flow는 팀 생산성과 품질을 동시에 높여줄 수 있는 브랜치 전략입니다.

 

 

 

'Git > GitHub' 카테고리의 다른 글

Git Flow 전략 - 실전 예시  (0) 2025.05.03
GitHub - Image 올리기 (README, Issue, PR)  (0) 2021.08.22

🧷 @PostConstruct란? - 스프링 빈 생성 이후 초기화 로직 실행

스프링 기반 애플리케이션을 개발하다 보면, 빈이 생성된 직후 한 번만 실행되어야 하는 초기화 작업이 필요할 때가 있습니다.

이럴 때 사용할 수 있는 간단하고 강력한 도구가 바로 @PostConstruct 어노테이션입니다.


✅ @PostConstruct란?

  • Java에서 제공하는 표준 애노테이션(JSR-250)
  • 스프링 빈이 생성된 직후(의존성 주입 완료 후)에 실행할 초기화 메서드에 붙입니다.
  • 별도의 XML 설정 없이, 메서드에 어노테이션만 붙이면 자동 호출됩니다.

🛠 사용 예제

@Component
public class SampleService {

    @PostConstruct
    public void init() {
        System.out.println("SampleService 초기화 완료!");
    }
}

실행 흐름

  1. SampleService 빈 생성됨
  2. 생성자 실행됨
  3. 의존성 주입 완료됨
  4. @PostConstruct가 붙은 init() 메서드 실행됨 ✅

🔄 왜 쓰는 걸까?

  • 생성자에서는 의존성 주입이 완료되지 않았기 때문에, 초기화 코드를 넣기 어려움
  • 빈이 준비된 이후 실행해야 하는 코드 (ex: 캐시 로딩, 외부 API 호출, 로그 등)는 @PostConstruct가 적합

⛔ 주의사항

항목 설명
매개변수 없음 @PostConstruct 메서드는 반드시 파라미터가 없어야 합니다
반환값 없음 void 타입이어야 함
한 번만 실행 스프링이 빈을 초기화할 때 딱 한 번만 호출
메서드는 한 개 권장 여러 개 있어도 되지만, 복잡성 증가로 인해 1개만 사용하는 것이 일반적

🔁 @PostConstruct vs InitializingBean.afterPropertiesSet()

항목 @PostConstruct InitializingBean
선언 방식 어노테이션 기반 인터페이스 구현 기반
구현 부담 적음 높음 (의존성 생김)
스프링 의존도 낮음 (자바 표준) 높음 (Spring 인터페이스)
추천 여부 ✅ 일반적으로 권장 ❌ 특별한 경우에만 사용

⚙️ 언제 사용하면 좋을까?

  • DB에서 설정 값을 미리 로드하고 싶은 경우
  • 외부 API에서 데이터를 미리 가져와야 할 경우
  • 정적 리소스를 초기화할 경우
  • 한 번만 수행돼야 할 초기화 로직이 있을 때

🔄 자주 묻는 질문

❓ 생성자랑 뭐가 다른가요?

  • 생성자는 의존성 주입이 완료되기 전에 실행됩니다.
    → 따라서 @Autowired된 필드는 아직 null일 수 있어요.
  • @PostConstruct는 모든 주입이 끝난 직후 실행되므로, 안정적인 초기화가 가능합니다.

✅ 정리

항목 설명
목적 스프링 빈 초기화 후 한 번 실행될 로직 작성
실행 시점 의존성 주입 완료 후
제한 파라미터 없음, 반환값 없음 (void)
대안 InitializingBean, @Bean(initMethod="...") 등
추천 여부 일반적인 경우엔 @PostConstruct가 가장 간편하고 명확함 ✅

📌 보너스: Spring 6+ / Jakarta 전환 관련

Spring 6부터는 @PostConstruct가 Jakarta 패키지로 이전됨:

import jakarta.annotation.PostConstruct;

 

기존 코드에서 javax.annotation.PostConstruct를 사용 중이라면 Jakarta EE로 마이그레이션이 필요할 수 있습니다.

 

🧩 자바의 필드(Field)와 프로퍼티(Property) 차이 완벽 정리 - getter/setter와 실제 변수, 무엇이 다를까?

 

자바로 객체 지향 프로그래밍을 하다 보면 "필드"와 "프로퍼티"라는 용어를 자주 접하게 됩니다.
두 용어가 비슷하게 사용되지만, 의미와 쓰임은 명확히 다릅니다.
이번 글에서는 이 둘의 개념과 차이를 정확히 정리해보겠습니다.


✅ 필드(Field)란?

필드는 클래스 내부에 정의된 멤버 변수(실제 데이터 저장소)입니다.
객체가 상태(state)를 가지기 위해 보유하는 값이며, 보통 private으로 선언됩니다.

public class Person {
    private String name; // ← 이게 필드!
}
  • name은 클래스 안에 존재하는 실제 데이터 공간
  • 클래스 내부에서 this.name처럼 직접 접근 가능

✅ 프로퍼티(Property)란?

프로퍼티는 객체 외부에서 getter/setter 메서드를 통해 간접적으로 접근하는 속성입니다.
즉, 필드와 연결된 외부 공개 인터페이스라고 볼 수 있습니다.

public class Person {
    private String name;

    public String getName() {
        return name; // ← 이게 name이라는 "프로퍼티"
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • getName()과 setName() 메서드는 name 필드의 프로퍼티 역할
  • 자바빈(JavaBeans) 규약에서는 이 메서드 집합을 "name 프로퍼티"라고 부릅니다.

📊 필드 vs 프로퍼티 비교 정리

구분 필드(Field) 프로퍼티(Property)
정의 클래스 내부에 선언된 멤버 변수 필드에 접근하기 위한 getter/setter 메서드 집합
접근 방식 직접 접근 (this.name) 간접 접근 (getName(), setName())
존재 유무 실제로 메모리에 존재 개념적으로 존재 (인터페이스로서의 접근 수단)
사용 목적 객체의 내부 상태 저장 외부에서 안전하게 상태를 읽거나 수정할 수 있도록 함
예시 private String name; getName(), setName(String name)

⚠️ 헷갈리지 마세요!

❗ "필드 = 변수", "프로퍼티 = 변수 + 접근자(getter/setter)" 개념입니다.
프로퍼티는 "상태" 자체라기보단, 상태를 다루는 접근 수단에 가까워요.


🛠️ 실무에서 왜 중요할까?

📌 예시 1: JPA에서의 접근 전략

JPA에서는 데이터를 읽고 쓸 때 어떻게 접근할지를 선택할 수 있습니다:

  • 필드 접근(Field Access) → 필드에 직접 접근
  • 프로퍼티 접근(Property Access) → getter/setter를 통해 접근
@Access(AccessType.PROPERTY)
@Entity
public class Member {

    private String name;

    @Column(name = "member_name")
    public String getName() {
        return name; // ← 프로퍼티 접근 방식
    }
}

✔ JPA는 이 getName()을 통해 name 필드를 읽고 씁니다.
따라서 어디에 어노테이션을 붙이느냐에 따라 접근 방식이 달라집니다.


📌 예시 2: JSON 직렬화 (Jackson, Gson 등)

Jackson도 기본적으로 getter를 기준으로 직렬화/역직렬화합니다.

public class User {
    private String name;

    public String getName() {
        return name; // ← 이걸 통해 JSON에 포함됨
    }
}

✔ 필드에 직접 접근할 수도 있지만, 설정에 따라 다르기 때문에 "필드냐, 프로퍼티냐"를 명확히 구분해야 합니다.


✅ 마무리 정리

항목 필드 프로퍼티
존재 방식 실제 객체 내부의 변수 getter/setter를 통해 노출되는 속성
객체 내부 사용 this.name 등으로 직접 접근 내부보단 외부에서 접근 시 활용
프레임워크 사용 시 JPA, Jackson 등에서 접근 방식 차이 발생 getter/setter에 따라 매핑 방식 달라짐

👉 필드 = 저장소, 프로퍼티 = 인터페이스
이 구분만 정확히 기억해도 실무에서 큰 도움이 됩니다.

 

 

 

🌐 @RequestBody, @ResponseBody vs HttpEntity(RequestEntity, ResponseEntity)
- 스프링에서 HTTP Body를 처리하는 5가지 방식 정리

 

REST API를 개발하다 보면 HTTP 요청과 응답의 본문(body)을 직접 다뤄야 할 일이 많습니다.

이때 사용할 수 있는 방법이 여러 가지 있는데, 대표적으로 다음과 같은 것들이 있습니다.

 

@RequestBody, @ResponseBody, HttpEntity, RequestEntity, ResponseEntity

 

이름이 비슷해서 헷갈리기 쉽지만, 의도와 활용 방식에 차이가 있습니다.

이번 글에서는 이 5가지의 차이를 명확하게 비교해보겠습니다.


1️⃣ @RequestBody란?

HTTP 요청의 body 내용을 자바 객체로 변환해주는 어노테이션입니다.
→ JSON, XML, plain text 등을 객체로 바꿔줍니다.

@PostMapping("/hello")
public String hello(@RequestBody UserDto user) {
    return "Hello " + user.getName();
}

특징

  • 요청 본문(JSON 등)을 객체로 바로 변환
  • 내부적으로 HttpMessageConverter가 동작
  • 필수 값 (기본적으로 body가 없으면 400 Bad Request 발생)

2️⃣ @ResponseBody란?

자바 객체를 HTTP 응답의 body로 직접 변환해주는 어노테이션입니다.
→ JSON, XML 등으로 자동 직렬화됩니다.

@GetMapping("/user")
@ResponseBody
public UserDto getUser() {
    return new UserDto("철준", 35);
}

특징

  • 반환값이 뷰 이름이 아닌 데이터 자체로 응답
  • @RestController를 쓰면 클래스 전체에 @ResponseBody 적용됨

3️⃣ HttpEntity<T>란?

요청 또는 응답에서 HTTP 본문(body) + 헤더 정보를 함께 가져오거나 응답할 수 있는 객체입니다.

@PostMapping("/entity")
public String handle(HttpEntity<UserDto> request) {
    HttpHeaders headers = request.getHeaders();
    UserDto body = request.getBody();
    ...
}

특징

  • 본문과 헤더를 동시에 받을 수 있음
  • 파라미터로만 사용 가능 (리턴 시에는 ResponseEntity를 씀)

4️⃣ RequestEntity<T>란?

HttpEntity를 상속한 클래스이며, 요청 메서드, URL, 헤더, 본문 등 더 많은 정보를 포함합니다.

 
@PostMapping("/request-entity")
public String handleRequest(RequestEntity<UserDto> request) {
    HttpMethod method = request.getMethod();
    URI url = request.getUrl();
    UserDto body = request.getBody();
    ...
}
  • HttpEntity + HTTP method, URL 정보 포함
  • 요청 정보를 보다 정밀하게 다루고 싶을 때 사용

5️⃣ ResponseEntity<T>란?

응답의 본문 + 상태 코드 + 헤더를 직접 제어할 수 있는 객체입니다.
→ 가장 많이 사용되는 응답 타입

@GetMapping("/response")
public ResponseEntity<UserDto> response() {
    UserDto user = new UserDto("철준", 35);
    return ResponseEntity
        .status(HttpStatus.CREATED)
        .header("Custom-Header", "hello")
        .body(user);
}

특징

  • HTTP 상태 코드, 헤더, 바디를 자유롭게 설정 가능
  • REST API 응답 커스터마이징에 최적

📊 전체 비교표

항목본문 처리헤더 접근상태 코드 설정사용 위치비고

 

항목 본문 처리 헤더 접근 상태 코드 설정 사용 위치 비고
@RequestBody 파라미터 요청 본문을 객체로 변환
@ResponseBody 반환값 객체를 JSON 등으로 응답
HttpEntity<T> 파라미터 헤더 + 바디 접근 가능
RequestEntity<T> 파라미터 HttpMethod, URL 정보 포함
ResponseEntity<T> 반환값 응답 제어용으로 자주 사용

💡 정리 포인트

  • @RequestBody는 클라이언트 → 서버 요청 본문(JSON 등)을 객체로 바꾸는 도구
  • @ResponseBody는 서버 → 클라이언트 응답을 객체 → JSON 등으로 바꾸는 도구
  • HttpEntity는 본문 + 헤더 모두 보고 싶을 때 사용
  • RequestEntity는 요청의 HTTP 메서드, URI까지 알고 싶을 때
  • ResponseEntity는 응답의 상태 코드, 헤더, 본문 모두 직접 조작할 수 있을 때 사용

✨ 마무리

Spring MVC에서 HTTP 요청과 응답을 다룰 때는 상황에 맞는 도구를 선택하는 것이 중요합니다.

  • 단순 요청이라면 @RequestBody, @ResponseBody로 간단하게 처리
  • 헤더나 상태 코드까지 조작이 필요하다면 HttpEntity, ResponseEntity를 활용하자.

 

 


 

 

 

🧰 HttpEntity, RequestEntity, ResponseEntity는 Body만 다룰까?
- HTTP 요청/응답의 전체 구조를 함께 다루는 '컨테이너' 입니다.

 

Spring MVC에서 HttpEntity, RequestEntity, ResponseEntity는 종종 "HTTP Body를 처리할 때 사용하는 객체"로 소개됩니다.

하지만 이 클래스들은 본문(body)뿐 아니라 헤더(header)까지 포함해서, 더 넓은 역할을 수행합니다.

이 클래스들은 "HTTP 요청/응답을 구성하는 모든 정보(본문, 헤더, 상태 코드 등)를 함께 담는 컨테이너"입니다.

📦 HTTP 요청/응답은 어떤 정보로 구성돼 있을까?

HTTP 메시지는 크게 다음 3가지로 구성됩니다:

  1. 상태 라인 / 요청 라인 (Request: GET /hello, Response: HTTP/1.1 200 OK)
  2. 헤더(Header) — Content-Type, Authorization 등
  3. 본문(Body) — JSON, XML, 텍스트 등 실질적인 데이터

☝️ 여기서 핵심 질문:

"스프링에서는 이 모든 걸 어떻게 한 번에 다룰 수 있을까?"

 

그때 등장하는 것이 바로:

  • HttpEntity<T>
  • RequestEntity<T>
  • ResponseEntity<T>

이들은 단순히 Body만 처리하는 것이 아니라,
HTTP 메시지를 이루는 구조 전체를 객체로 감싸서 통합적으로 다루는 도구입니다.


1️⃣ HttpEntity<T> - 요청/응답의 헤더 + 바디를 함께 담는 기본 컨테이너

public class HttpEntity<T> {
    private final HttpHeaders headers;
    private final T body;
}
  • headers: 요청 또는 응답의 HTTP 헤더
  • body: JSON 등 본문 데이터

✔ 특징

  • 요청과 응답 양쪽에서 사용 가능
  • 상태 라인(메서드, 상태코드)은 포함하지 않음

2️⃣ RequestEntity<T> - 요청 전체 정보를 모두 담고 싶은 경우

public class RequestEntity<T> extends HttpEntity<T> {
    private final HttpMethod method;
    private final URI url;
}
  • HttpEntity의 기능 + 요청 라인(HTTP 메서드, URL) 정보까지 포함
  • 예: POST /api/users, GET /api/data

✔ 특징

  • HTTP 요청 메시지의 구조 전체를 객체로 표현 가능
  • 요청 처리 전에 메서드나 URI 조건을 확인하고 분기할 수 있음

3️⃣ ResponseEntity<T> - 응답 전체 구조를 구성하고 반환하는 객체

public class ResponseEntity<T> extends HttpEntity<T> {
    private final HttpStatus status;
}
  • HttpEntity의 기능 + 응답 상태 코드(200 OK, 404 Not Found 등)까지 포함

✔ 특징

  • 바디, 헤더, 상태 코드 모두 커스터마이징 가능
  • 실제 운영 API에서 가장 자주 쓰이는 반환 타입

📊 전체 비교 정리

항목 바디 헤더 상태 코드 라인 정보(method, URL 등) 사용 목적
HttpEntity<T> 요청/응답 헤더+바디 처리
RequestEntity<T> 요청의 메서드, URL까지 필요할 때
ResponseEntity<T> 응답의 상태 코드까지 제어하고 싶을 때

 


💡 실전에서 언제 쓰일까?

요청 처리할 때

@PostMapping("/auth")
public String auth(HttpEntity<?> request) {
    HttpHeaders headers = request.getHeaders();
    // Authorization, User-Agent 등 확인 가능
    ...
}

→ 요청의 헤더와 바디를 함께 확인해야 할 때 유용


응답 보낼 때

@GetMapping("/download")
public ResponseEntity<Void> download() {
    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment; filename=data.csv");
    return new ResponseEntity<>(headers, HttpStatus.OK);
}

본문 없이도 헤더와 상태코드를 명확하게 제어 가능


✨ 마무리 요약

  • HttpEntity, RequestEntity, ResponseEntity는 단순한 Body 전달용이 아님
    • → HTTP 메시지의 모든 구조(본문, 헤더, 상태 코드, 요청 메서드 등)를 객체 형태로 다루는 도구
  • 상황에 따라 필요한 정보를 얼마나 정밀하게 제어할 것인지에 따라 적절한 클래스를 선택하면 됩니다.

 

 

 

 

🏷️ DTYPE 컬럼

상속 매핑에서 사용하는 구분 컬럼 (Discriminator Column)

JPA에서는 엔티티 간에 상속 관계를 매핑할 수 있습니다. 그리고 이때 사용되는 특별한 컬럼이 바로 DTYPE입니다.
처음에는 생소할 수 있지만, 그 역할을 이해하면 굉장히 유용한 기능입니다.


1️⃣ DTYPE이란?

DTYPE은 JPA에서 상속 매핑 시, 어떤 하위 엔티티 타입인지 구분하기 위해 자동 생성되는 컬럼입니다.

쉽게 말해, 하나의 테이블에 여러 타입의 자식 엔티티 데이터를 함께 저장할 때, "이 레코드는 어떤 자식 클래스에 해당하는지 알려주는 표시"예요.

예를 들어:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}
@Entity
public class Book extends Item {
    private String author;
}
@Entity
public class Album extends Item {
    private String artist;
}

이렇게 상속 구조가 있을 때, JPA는 Item 테이블을 만들고, Book과 Album 데이터를 같은 테이블에 저장합니다.

이때 "이 데이터가 Book인지 Album인지 어떻게 구분할까?"
→ 그걸 위해 생기는 컬럼이 바로 DTYPE입니다.


2️⃣ 생성되는 테이블 예시

id name author artist DTYPE
1 자바의 정석 남궁성 null Book
2 힙합 앨범 null 박재범 Album
  • JPA가 자동으로 DTYPE 컬럼을 만들고, 해당 엔티티의 클래스 이름을 저장합니다.
  • 이 정보는 JPA가 조회할 때, 어떤 클래스로 데이터를 변환할지 결정하는 데 사용됩니다.

3️⃣ @DiscriminatorColumn으로 이름 변경 가능

기본적으로 DTYPE이라는 컬럼명이 생성되지만, 직접 이름을 지정할 수도 있어요.

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "item_type")
public abstract class Item {
    ...
}

이렇게 하면 item_type이라는 이름으로 컬럼이 생성됩니다.


4️⃣ DTYPE이 사용되는 상속 전략

상속 전략 DTYPE 사용 여부 설명
SINGLE_TABLE (단일 테이블 전략) ✅ 사용함 모든 자식 엔티티를 한 테이블에 저장하므로 반드시 필요
JOINED (조인 전략) ✅ 사용함 테이블은 나뉘지만, 부모 테이블에 구분값이 필요함
TABLE_PER_CLASS (각자 테이블 전략) ❌ 사용 안 함 테이블이 완전히 분리되므로 필요 없음

 

5️⃣ 조회할 때 어떻게 쓰이나요?

Item item = entityManager.find(Item.class, 1L);
// DTYPE 컬럼이 Book이면 Book 객체로 자동 매핑됨
  • DTYPE 값이 Book이면 Book 객체로, Album이면 Album 객체로 자동 변환됩니다.
  • 즉, DTYPE은 JPA의 다형성 구현을 위한 핵심 키워드입니다.

✅ 요약

항목 내용
역할 상속 관계에서 실제 객체 타입을 구분하기 위한 컬럼
기본 컬럼명 DTYPE
커스터마이징 @DiscriminatorColumn(name = "구분값")
사용 전략 SINGLE_TABLE, JOINED 전략에서 사용됨
값의 의미 실제 저장된 자식 클래스 이름 (기본값)

 

✨ 마무리

JPA에서 DTYPE은 상속 매핑의 다형성 지원을 위한 중요한 컬럼입니다.

무심코 생성된 것처럼 보일 수 있지만, 실제로는 자식 엔티티를 구분하고 올바른 타입으로 조회하는 데 핵심적인 역할을 합니다.

실무에서 상속 매핑을 사용할 일이 있다면 꼭 알아두면 좋습니다.

 

 

+ Recent posts