◇ PART · REST

JSON 형식 깊이

자바 객체 ↔ JSON 변환의 마법

학습 목표

  • JSON 의 문법을 안다
  • 자바 객체와 JSON 의 매핑 규칙을 안다
  • Jackson 의 자동 변환 동작을 안다
  • 자주 만나는 JSON 함정 (날짜·null·순환 참조)

🛠️ JSON — 데이터의 공용어

JavaScript Object Notation

JS 객체 표기법에서 출발한 가벼운 텍스트 데이터 형식. 모든 언어가 읽고 쓸 수 있어 — 시스템 사이의 「공용어」.

JSON 의 6 가지 자료형

타입예시
문자열"홍길동"
숫자42, 3.14
불린true, false
nullnull
배열[1, 2, "a"]
객체{"key": "value"}

👉 6 가지가 끝. 단순함이 강점.

JSON 예시 — 게시글


{
    "num": 3,
    "title": "첫 글입니다",
    "content": "안녕하세요",
    "writer": "hong",
    "viewCount": 0,
    "createdAt": "2024-08-15T10:30:00",
    "tags": ["spring", "java"],
    "replies": [
        {"num": 1, "content": "환영합니다"},
        {"num": 2, "content": "잘 보고 갑니다"}
    ]
}

👉 객체 안에 배열·중첩 객체 — 자유롭게 조합 가능.

자바 ↔ JSON 매핑

자바JSON
String"문자열"
int / long / double숫자
booleantrue / false
nullnull
List · 배열[ ... ]
Map · 객체{ "key": value }
LocalDateTime"2024-08-15T10:30:00" (ISO 8601)
LocalDate"2024-08-15"

Jackson — Spring 의 기본 JSON 라이브러리

Jackson = 자바 진영의 표준 JSON 라이브러리. Spring MVC 의 기본 의존성에 자동 포함.

  • 객체 → JSON 자동 변환 (Serialize)
  • JSON → 객체 자동 변환 (Deserialize)
  • 중첩 / List / Map 모두 처리
  • 날짜 형식·null 처리 등 옵션 가능

자동 변환 — 내부 동작

자바 객체 → JSON ────────────────── public class Board { private int num; private String title; } ↓ Jackson 이: - 모든 getter 호출 (getNum, getTitle) - 메서드명에서 「get」 제거 + 첫 글자 소문자 - 결과를 JSON 객체로 조립 ↓ {"num": 3, "title": "첫 글"} JSON → 자바 객체 ────────────────── {"num": 3, "title": "첫 글"} ↓ Jackson 이: - 기본 생성자로 새 Board 객체 생성 - 각 키마다 setter 호출 (setNum, setTitle) ↓ Board { num=3, title="첫 글" }

날짜·시간 처리


public class Board {
    private LocalDateTime createdAt;
}

// JSON 출력:
{"createdAt": "2024-08-15T10:30:00"}

// 한국어 형식 원하면 — @JsonFormat
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime createdAt;

// → "2024-08-15 10:30"
옛 Date 클래스

java.util.Date 는 타임존 처리가 까다로움. 가능하면 LocalDateTime · Instant 사용.

민감 정보 제외 — @JsonIgnore


public class Member {
    private String id;
    private String nick;

    @JsonIgnore           // ⭐ 응답에 포함 안 함
    private String pwd;
}

// 응답 JSON:
{"id": "hong", "nick": "홍길동"}
// pwd 없음

👉 비밀번호 같은 민감 정보가 실수로 응답에 포함되는 것을 방지.

순환 참조 함정

⚠️ Board ↔ Reply 양방향 참조

public class Board {
    private List<Reply> replies;  // 게시글이 댓글들을
}

public class Reply {
    private Board board;          // 댓글이 게시글을
}

Jackson 이 변환 시 — Board → replies → Board → replies → ... 무한 루프 → StackOverflow.

해결: @JsonManagedReference + @JsonBackReference, 또는 응답 전용 DTO 분리.

응답 DTO 패턴 — DTO 클래스


// 도메인 객체 (DB 매핑)
public class Board { ... }

// 응답 전용 DTO (REST API 응답)
public class BoardResponse {
    private int num;
    private String title;
    private String writer;
    // pwd 없음, 민감 필드 없음

    public static BoardResponse from(Board b) {
        BoardResponse r = new BoardResponse();
        r.num = b.getNum();
        r.title = b.getTitle();
        r.writer = b.getWriter();
        return r;
    }
}

👉 응답 전용 클래스를 따로 두면 노출 필드를 명시적으로 통제할 수 있습니다.

응답 DTO 패턴 — Controller 사용


// Controller
@GetMapping
public List<BoardResponse> list() {
    return service.selectList().stream()
        .map(BoardResponse::from)
        .toList();
}

👉 도메인 객체를 그대로 노출하지 않고, 응답 DTO 로 한번 변환해서 내보냅니다.

JS 측 처리


// 받기 — JSON → 객체
const res = await fetch('/api/boards/3');
const board = await res.json();
console.log(board.title);   // myboard.num=3 의 제목

// 보내기 — 객체 → JSON
await fetch('/api/boards', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
        title: '새 글',
        content: '안녕하세요'
    })
});

실수 모음

증상원인
"Cannot construct instance"VO 에 기본 생성자 없음
필드값이 nullsetter 없음 또는 이름 불일치
StackOverflowError순환 참조
날짜 형식 이상@JsonFormat 미설정
비밀번호 응답에 포함@JsonIgnore 누락

🔄 Before / After

전 차시 끝

JSON 이 「데이터 표기법」 정도라는 것만 안다.

이번 차시 끝

JSON ↔ 자바 매핑 규칙을 알고 흔한 함정을 안다.

정리

오늘 들고 가는 것

  • JSON 6 자료형 (문자·숫자·불린·null·배열·객체)
  • Jackson 의 자동 매핑 (getter/setter 활용)
  • @JsonIgnore = 민감 필드 제외
  • 순환 참조 — DTO 분리로 해결
  • @JsonFormat = 날짜 형식