🧷 @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은 상속 매핑의 다형성 지원을 위한 중요한 컬럼입니다.

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

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

 

 

💾 CLOB vs BLOB - 대용량 데이터를 저장하는 두 가지 방식

 

RDBMS(관계형 데이터베이스)에서는 종종 텍스트나 파일 같은 대용량 데이터를 다뤄야 할 때가 있습니다.
이럴 때 사용하는 대표적인 데이터 타입이 바로 CLOB(Character Large Object)와 BLOB(Binary Large Object)입니다.
이번 글에서는 이 둘의 차이와 활용 방법에 대해 정리해 보겠습니다.


📌 1. CLOB이란?

CLOB(Character Large Object)는 대용량 텍스트 데이터를 저장하기 위한 데이터 타입입니다.

예시

  • 장문의 기사 본문
  • JSON/XML 형식의 긴 설정값
  • 문서 내용 (예: 계약서, 보고서 등)

특징

  • 문자열(문자 기반) 데이터 전용
  • 일반적으로 최대 수십 MB~수 GB까지 저장 가능 (DBMS마다 다름)
  • JDBC에서는 java.sql.Clob으로 처리됨

📌 2. BLOB이란?

BLOB(Binary Large Object)는 이미지, 동영상, PDF, 실행파일 등 바이너리 데이터를 저장하는 타입입니다.

예시

  • 이미지 파일 (JPG, PNG 등)
  • 동영상 파일
  • 바이너리 파일(exe, zip 등)

특징

  • 비정형(바이너리) 데이터 전용
  • 텍스트가 아닌 데이터는 반드시 BLOB을 사용
  • JDBC에서는 java.sql.Blob으로 처리됨

📊 CLOB vs BLOB 비교 정리

구분 CLOB BLOB
의미 Character Large Object Binary Large Object
용도 긴 텍스트 저장 이미지, 파일 등 바이너리 저장
데이터 형태 문자 기반 (UTF-8 등 인코딩) 바이너리 기반 (byte)
JDBC 타입 java.sql.Clob java.sql.Blob
처리 방식 Reader / Writer InputStream / OutputStream
주 사용 예시 뉴스 본문, 소설, XML, JSON 사진, 동영상, PDF, ZIP 등

🛠️ 사용 예시 (JDBC 기준)

✅ CLOB 저장

PreparedStatement ps = conn.prepareStatement("INSERT INTO article (title, content) VALUES (?, ?)");
ps.setString(1, "클롭 예제");
ps.setClob(2, new StringReader("긴 텍스트 내용..."));
ps.executeUpdate();

✅ BLOB 저장

PreparedStatement ps = conn.prepareStatement("INSERT INTO files (filename, data) VALUES (?, ?)");
ps.setString(1, "image.jpg");
ps.setBlob(2, new FileInputStream(new File("image.jpg")));
ps.executeUpdate();

 


💡 스프링(Spring)에서는 어떻게 다룰까?

Spring JDBC 또는 JPA에서도 CLOB/BLOB 처리는 가능합니다.

JPA 예시

@Lob
private String content; // CLOB 처리됨

@Lob
private byte[] fileData; // BLOB 처리됨
  • @Lob 어노테이션은 필드를 CLOB 또는 BLOB으로 자동 매핑합니다.
  • String이면 CLOB, byte[]이면 BLOB으로 판단함

⚠️ 주의할 점

  1. 성능 이슈: CLOB/BLOB은 많은 I/O를 유발하므로 트래픽이 많은 서비스에서 남용은 금물입니다.
  2. 파일 vs DB 저장 고민:
    • 파일을 DB에 직접 저장하지 않고, 파일 경로만 저장하고 실제 파일은 서버나 S3 등에 저장하는 방식도 많이 씁니다.
  3. DBMS별 제한: Oracle, MySQL, PostgreSQL 등 DB마다 최대 크기와 처리 방식이 다르니 문서 확인 필수입니다.
  4. 트랜잭션 크기: 너무 큰 BLOB 데이터를 저장하거나 수정할 경우 트랜잭션 크기 제한에 걸릴 수 있음

📚 정리

질문 답변
텍스트 저장하려면? CLOB
파일/이미지 저장하려면? BLOB
문자 기반인가요? CLOB는 문자, BLOB은 바이너리
JPA에서는 어떻게? @Lob + String 또는 byte[]
직접 DB에 저장해도 되나요? 가능하지만, 파일 시스템 저장이 더 효율적일 수 있음

 

📝 마무리

CLOB과 BLOB은 데이터베이스에서 대용량 데이터를 다룰 때 반드시 알아야 할 개념입니다.

용도에 맞게 잘 활용하면 유용하지만, 과도하게 사용하면 성능 문제를 초래할 수 있습니다.

실제 프로젝트에서는 "직접 DB에 저장할지, 파일로 분리할지" 고민한 후 선택하는 것이 중요합니다.

 

🧩 ObjectMapper란? - 자바에서 JSON을 다루는 가장 강력한 도구

웹 개발에서 JSON은 가장 많이 사용되는 데이터 포맷입니다.

특히 Spring Boot를 사용하면 REST API에서 JSON은 기본값이죠.

이때 자바 객체 ↔ JSON 간 변환을 손쉽게 도와주는 것이 바로 Jackson의 ObjectMapper입니다.


1️⃣ ObjectMapper란?

ObjectMapper는 Jackson 라이브러리에서 제공하는 클래스이며, 자바 객체를 JSON으로 직렬화하거나(JSON 생성), JSON 문자열을 자바 객체로 역직렬화할 때(JSON 파싱) 사용됩니다.

com.fasterxml.jackson.databind.ObjectMapper

2️⃣ 주요 기능

기능 설명
직렬화 (Serialization) 자바 객체 → JSON 문자열
역직렬화 (Deserialization) JSON 문자열 → 자바 객체
읽기/쓰기 지원 파일, InputStream, 문자열, 바이트 등 다양한 입력/출력 지원
유연한 설정 가능 필드 명명 전략, Null 처리, 날짜 포맷 등 커스터마이징 가능

3️⃣ 사용 예제

✅ JSON → 자바 객체 (Deserialization)

String json = "{\"name\":\"철준\",\"age\":35}";

ObjectMapper objectMapper = new ObjectMapper();
Person person = objectMapper.readValue(json, Person.class);

System.out.println(person.getName()); // 철준
public class Person {
    private String name;
    private int age;
    
    // 기본 생성자 & getter/setter 필요!
}

✅ 자바 객체 → JSON (Serialization)

Person person = new Person();
person.setName("철준");
person.setAge(35);

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(person);

System.out.println(json); 
// 결과: {"name":"철준","age":35}

4️⃣ 자주 쓰는 메서드 정리

메서드 설명
writeValueAsString(Object obj) 객체 → JSON 문자열
writeValue(File/file/OutputStream, Object obj) 객체를 파일 또는 스트림에 JSON으로 저장
readValue(String json, Class<T> clazz) JSON 문자열 → 객체
readTree(String json) JSON 문자열을 트리 구조(JsonNode)로 파싱
convertValue(Object fromValue, Class<T> toValueType) 객체 간 타입 변환 (ex. DTO ↔ Map)

5️⃣ JSON 트리 다루기 (JsonNode)

단순한 매핑이 아니라, 노드 단위로 JSON을 탐색하고 싶을 때는 readTree()를 사용합니다.

String json = "{\"user\":{\"name\":\"철준\",\"age\":35}}";
JsonNode root = objectMapper.readTree(json);
String name = root.path("user").path("name").asText();

System.out.println(name); // 철준

6️⃣ 주의할 점

  • 객체를 JSON으로 변환하려면 기본 생성자와 getter/setter가 반드시 필요합니다.
  • 필드명이 JSON 키와 다를 경우 @JsonProperty("jsonKey")를 이용해야 합니다.
  • 날짜, null 처리 등은 기본 설정이 예상과 다를 수 있으므로 커스터마이징이 필요할 수 있습니다.
@JsonProperty("user_name")
private String userName;

7️⃣ Spring Boot에서 자동으로 쓰이는 이유

Spring Boot는 내부적으로 Jackson의 ObjectMapper를 기본 메시지 컨버터로 사용합니다.
즉, @RestController에서 return 객체를 하면 자동으로 JSON으로 변환해주는 이유도 이 때문입니다.

@GetMapping("/user")
public Person user() {
    return new Person("철준", 35); // 자동으로 JSON 변환됨
}

✅ 마무리

ObjectMapper는 JSON을 다룰 때 없어서는 안 될 핵심 도구입니다.
직렬화/역직렬화는 물론이고, 유연한 커스터마이징, 트리 탐색, 타입 변환 등 다양한 기능을 제공합니다.
특히 Spring Boot에서는 기본값으로 쓰이기 때문에, 잘 익혀두면 JSON 처리에서 자유로워질 수 있습니다.


 

참조

+ Recent posts