๐Ÿ’ป PROJECT/[Spring Boot, React] ๋…์„œ ์Šต๊ด€ ๊ด€๋ฆฌ ์„œ๋น„์Šค

[ํ”„๋กœ์ ํŠธ] ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท ์„ค๊ณ„

devCloud 2026. 4. 30. 14:18
728x90
PROJECT

๊ณตํ†ต ์‘๋‹ต ํฌ๋งท ์„ค๊ณ„

01. ๋ฐฐ๊ฒฝ

ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์„ธํŒ… ๋‹จ๊ณ„์—์„œ ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท์„ ์„ค๊ณ„ํ–ˆ๋‹ค. ์ˆ˜์—…์—์„œ ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ์ ‘ํ–ˆ๊ณ , ์ง์ ‘ ์ ์šฉํ•ด๋ณด๋‹ˆ ์ผ๊ด€์„ฑ ์žˆ๋Š” API ์‘๋‹ต ๊ตฌ์กฐ๊ฐ€ ๊ฐœ๋ฐœ ์ „๋ฐ˜์— ๋„์›€์ด ๋œ๋‹ค๊ณ  ๋А๊ปด์„œ ์ด ํ”„๋กœ์ ํŠธ์—๋„ ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ–ˆ๋‹ค.

02. ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

API ์‘๋‹ต ํ˜•์‹์ด ์—”๋“œํฌ์ธํŠธ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค.

// ์–ด๋–ค API๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๊ณ 
{ "id": 1, "title": "ํด๋ฆฐ์ฝ”๋“œ" }

// ์–ด๋–ค API๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๊ณ 
{ "result": "success", "post": { "id": 1 } }

// ์—๋Ÿฌ๋Š” ๋˜ ์ด๋ ‡๊ฒŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค
{ "error": "Not Found" }

ํ”„๋ก ํŠธ์—”๋“œ ์ž…์žฅ์—์„œ๋Š” API๋งˆ๋‹ค ์‘๋‹ต ๊ตฌ์กฐ๊ฐ€ ๋‹ฌ๋ผ์„œ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ , ๋ฐฑ์—”๋“œ ์ž…์žฅ์—์„œ๋„ ์‘๋‹ต ํ˜•์‹์ด ํ†ต์ผ๋˜์ง€ ์•Š์•„ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์ง„๋‹ค. ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜ ์žฅ์ ์ด ์žˆ๋‹ค.

  • ํ”„๋ก ํŠธ์—”๋“œ-๋ฐฑ์—”๋“œ ๊ฐ„ ํ˜‘์—… ๋‹จ์ˆœํ™”: ๋ชจ๋“  API๊ฐ€ ๋™์ผํ•œ ๊ตฌ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋‹ˆ๊นŒ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‘๋‹ต ์ฒ˜๋ฆฌ ๋กœ์ง์„ ํ•˜๋‚˜๋กœ ํ†ต์ผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€๋ฅผ HTTP ์ƒํƒœ ์ฝ”๋“œ์—๋งŒ ์˜์กดํ•˜์ง€ ์•Š์•„๋„ ๋จ: success ํ•„๋“œ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋ฉด ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ์ผ๊ด€ํ™”: message ํ•„๋“œ๋กœ ํ†ต์ผํ•˜๋ฉด ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋””์„œ ๊บผ๋‚ด์•ผ ํ• ์ง€ ๋งค๋ฒˆ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ํ˜ผ๋ž€์ด ์—†์–ด์ง„๋‹ค.
  • ํ™•์žฅ์„ฑ: ๋‚˜์ค‘์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ApiResponse<T> ํ•˜๋‚˜๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ์ „์ฒด API์— ๋ฐ˜์˜๋œ๋‹ค.

03. ์„ค๊ณ„ ๊ฒฐ์ •

1. ApiResponse<T> — ๊ณตํ†ต ์‘๋‹ต ๋ž˜ํผ

๋ชจ๋“  API ์‘๋‹ต์„ ApiResponse<T>๋กœ ๋ž˜ํ•‘ํ•ด์„œ ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๊ด€๋œ ๊ตฌ์กฐ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. record๋ฅผ ํ™œ์šฉํ•ด ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ์„ค๊ณ„ํ–ˆ๋‹ค.

/**
์„œ๋น„์Šค ์ „์—ญ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ณตํ†ต API ์‘๋‹ต ๋ž˜ํผ.
๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ ์‘๋‹ต์€ ์ด ํด๋ž˜์Šค๋กœ ๋ž˜ํ•‘๋œ๋‹ค.
*/
public record ApiResponse<T>(boolean success, T data, String message) {
    /** ๋ฐ์ดํ„ฐ ์—†๋Š” ์„ฑ๊ณต ์‘๋‹ต */
    public static <T> ApiResponse<T> ok() {
        return new ApiResponse<>(true, null, null);
    }
    /** ๋ฐ์ดํ„ฐ ์žˆ๋Š” ์„ฑ๊ณต ์‘๋‹ต */
    public static <T> ApiResponse<T> ok(T data) {
        return new ApiResponse<>(true, data, null);
    }
    /** ์‹คํŒจ ์‘๋‹ต */
    public static <T> ApiResponse<T> fail(String message) {
        return new ApiResponse<>(false, null, message);
    }
}

2. record DTO — ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด

Java 16๋ถ€ํ„ฐ ๋„์ž…๋œ record๋ฅผ DTO์— ์ ๊ทน ํ™œ์šฉํ–ˆ๋‹ค. ๊ธฐ์กด ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ DTO ๋Œ€๋น„ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋ฅผ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. record๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ์ž, getter, equals(), hashCode(), toString()์„ ์ƒ์„ฑํ•ด์ฃผ๊ณ  ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ผ DTO๋กœ ์ ํ•ฉํ•˜๋‹ค.

// record ๊ธฐ๋ฐ˜ DTO
public record PostResponse(
    Long id,
    String title,
    String author
) { }

3. from() ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ — DTO ๋ณ€ํ™˜ ์ฑ…์ž„ ๋ถ„๋ฆฌ

์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ฑ…์ž„์„ DTO ์ž์‹ ์ด ๊ฐ–๋„๋ก from() ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. Service๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

public record PostResponse(Long id, String title, String author, String genre) {
    /** Post ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‘๋‹ต DTO๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. */
    public static PostResponse from(Post post) {
        return new PostResponse(
            post.getId(), post.getTitle(), post.getAuthor(), post.getGenre()
        );
    }
}

4. CustomException + ErrorCode — ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ผ๊ด€ํ™”

์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ CustomException๊ณผ ErrorCode๋ฅผ ํ•จ๊ป˜ ๋˜์ ธ GlobalExceptionHandler์—์„œ ์ผ๊ด„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

@ExceptionHandler(CustomException.class)
public ResponseEntity<ApiResponse<Void>> handleCustomException(CustomException e) {
    return ResponseEntity
        .status(e.getErrorCode().getStatus())
        .body(ApiResponse.fail(e.getErrorCode().getMessage()));
}

04. ์ •๋ฆฌ

๊ฒฐ์ • ์ด์œ 
ApiResponse<T> ๋ž˜ํผ ๋ชจ๋“  API ์‘๋‹ต ํ˜•์‹ ํ†ต์ผ
record DTO ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ œ๊ฑฐ, ๋ถˆ๋ณ€ ๊ฐ์ฒด
from() ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ DTO ๋ณ€ํ™˜ ์ฑ…์ž„ ๋ถ„๋ฆฌ, Service ๋‹จ์ˆœํ™”
CustomException + ErrorCode ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ผ๊ด€ํ™”, ์ „์—ญ ํ•ธ๋“ค๋ง
Last Sync: 2026-04-30 | Booktine Project Log
728x90