DevLog ๐Ÿ˜ถ

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์›น ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์…˜ ํšŒ๊ณ  ๋ณธ๋ฌธ

์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค/๋ ˆ๋ฒจ 2

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์›น ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์…˜ ํšŒ๊ณ 

dolmeng2 2023. 4. 20. 15:40

๋‘๋‘ ๊ณผ ์ง„ํ–‰ํ•œ ๋ ˆ๋ฒจ 2 ์ฒซ ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฏธ์…˜์ธ ์›น ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์…˜์ด๋‹ค :D

์–ด์ฉŒ๋‹ค ๋ณด๋‹ˆ 1๋“ฑ์œผ๋กœ ๋ ˆ๋ฒจ 2 ์ฒซ ๋ฏธ์…˜์„ ๋งˆ๋ฌด๋ฆฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค ๐Ÿ‘

 

๋ ˆ๋ฒจ 2์˜ ๋ชฉํ‘œ๋Š” ๋ฏธ์…˜์— ๋Œ€ํ•œ ํšŒ๊ณ ๊ธ€์ด ๋ฐ€๋ฆฌ์ง€ ์•Š๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋น ๋ฅด๊ฒŒ ํšŒ๊ณ  ๊ธ€์„ ์ž‘์„ฑํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

์ด๋ฒˆ ๋ฏธ์…˜์€ ์š”๊ตฌ์‚ฌํ•ญ์— ๋น„ํ•ด ๋ฏธ์…˜ ๊ธฐ๊ฐ„์ด ๊ธธ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ฏธ์…˜์„ ์—ฌ์œ ๋กญ๊ฒŒ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋‚จ์€ ๊ธฐ๊ฐ„ ๋™์•ˆ์—๋Š” ์Šคํ”„๋ง์— ๋Œ€ํ•ด์„œ ์ข€ ๊ณต๋ถ€ํ•ด๋ณด๋Š” ์‹œ๊ธฐ๋กœ ๊ฐ€์ ธ๋ณด๋ ค๊ณ  ํ•œ๋‹ค.


โœ”๏ธ ์ž‘์„ฑํ•œ ์ฝ”๋“œ

 

GitHub - Cl8D/jwp-racingcar: ๋ ˆ๋ฒจ 2 ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๊ฒŒ์ž„ ์›น ๊ตฌํ˜„ ์ €์žฅ์†Œ

๋ ˆ๋ฒจ 2 ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๊ฒŒ์ž„ ์›น ๊ตฌํ˜„ ์ €์žฅ์†Œ. Contribute to Cl8D/jwp-racingcar development by creating an account on GitHub.

github.com

- PR ์ œ์ถœ ์ดํ›„ ์กฐ๊ธˆ ๋” ์ˆ˜์ •ํ•œ ๋ถ€๋ถ„์ด ์žˆ์–ด์„œ ์•„๋งˆ 2์ฐจ PR์˜ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„๊ณผ ์กฐ๊ธˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค!

 

โœ”๏ธ 1์ฐจ PR

 

[1๋‹จ๊ณ„ - ์›น ์ž๋™์ฐจ ๊ฒฝ์ฃผ] ์ ธ๋‹ˆ(์ด์ง€์›) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. by Cl8D · Pull Request #61 · woowacourse/jwp-racing

์•ˆ๋…•ํ•˜์„ธ์š”, ์ฝ”์ฆˆ! 5๊ธฐ ๋ฐฑ์—”๋“œ ํฌ๋ฃจ ์ ธ๋‹ˆ์ž…๋‹ˆ๋‹ค ๐Ÿ™‡‍โ™€๏ธ ๋‘๋‘ ์ด๋ž‘ ๋ ˆ๋ฒจ 2 ์ฒซ ๋ฒˆ์งธ ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง„ํ–‰ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค! ๊ธฐ์กด์— ์ง„ํ–‰ํ–ˆ๋˜ ์ฝ”๋“œ์—์„œ ์ง€์ €๋ถ„ํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์กฐ๊ธˆ ๋ฆฌํŒฉํ„ฐ๋ง์„

github.com

 

โœ”๏ธ 2์ฐจ PR

 

[2๋‹จ๊ณ„ - ์›น ์ž๋™์ฐจ ๊ฒฝ์ฃผ] ์ ธ๋‹ˆ(์ด์ง€์›) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. by Cl8D · Pull Request #99 · woowacourse/jwp-racing

์•ˆ๋…•ํ•˜์„ธ์š”, ์ฝ”์ฆˆ! ์ ธ๋‹ˆ์ž…๋‹ˆ๋‹ค ๐Ÿ™‡‍โ™€๏ธ  ์—ฌ๋Ÿฌ ๋ฒˆ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ ๋ณด๊ณ  ์‹ถ์–ด์„œ ์ตœ๋Œ€ํ•œ ๋น ๋ฅด๊ฒŒ ๋ฆฌํŒฉํ„ฐ๋ง ๋ฐ ๊ตฌํ˜„์„ ์ง„ํ–‰ํ•˜์˜€์–ด์š”. ์ง€๋‚œ ๋ฒˆ 1๋‹จ๊ณ„ ๋ฏธ์…˜์„ ๋น ๋ฅด๊ฒŒ ํ”ผ๋“œ๋ฐฑ ํ•ด์ฃผ์‹  ๋•๋ถ„์ด์—์š” :D 1๋‹จ๊ณ„

github.com

 


โœ”๏ธ ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ

- ์ž๋™์ฐจ ๊ฒฝ์ฃผ ์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ ˆ๋ฒจ 1 ๋•Œ ์ง„ํ–‰ํ–ˆ๋˜ ์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ)

- ์›น ์š”์ฒญ/์‘๋‹ต ๊ตฌํ˜„ํ•˜๊ธฐ

: ์›น์„ ํ†ตํ•ด ๊ฒŒ์ž„์„ ํ”Œ๋ ˆ์ด ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.

 

Request

POST /plays HTTP/1.1
content-type: application/json; charset=UTF-8
host: localhost:8080

{
    "names": "๋ธŒ๋ฆฌ,ํ† ๋ฏธ,๋ธŒ๋ผ์šด",
    "count": 10
}

Response

HTTP/1.1 200 
Content-Type: application/json

{
    "winners": "๋ธŒ๋ฆฌ",
    "racingCars": [
        {
            "name": "๋ธŒ๋ฆฌ",
            "position": 9
        },
        {
            "name": "ํ† ๋ฏธ",
            "position": 7
        },
        {
            "name": "๋ธŒ๋ผ์šด",
            "position": 3
        },
    ]
}

 

- DB ์—ฐ๋™ํ•˜๊ธฐ (H2 DB ์—ฐ๋™)
DB์— ์ €์žฅ๋˜๋Š” ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
: ํ”Œ๋ ˆ์ด ํšŸ์ˆ˜(trialCount)
: ํ”Œ๋ ˆ์ด์–ด ๋ณ„ ์ตœ์ข… ์ด๋™ ๊ฑฐ๋ฆฌ (์ด๋ฆ„(name), ์ตœ์ข… ์œ„์น˜(position))
: ์šฐ์Šน์ž(winners)
: ํ”Œ๋ ˆ์ดํ•œ ๋‚ ์งœ/์‹œ๊ฐ„

 

- ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ์ด๋ ฅ ์กฐํšŒ API ๊ตฌํ˜„

: web application์—์„œ DB์— ์ €์žฅ๋œ ํ”Œ๋ ˆ์ด ์ด๋ ฅ์„ ์š”์ฒญํ•˜๋ฉด ์‘๋‹ตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

HTTP/1.1 200 
Content-Type: application/json

[
    {
        "winners": "๋ธŒ๋ฆฌ",
        "racingCars": [
            {
                "name": "๋ธŒ๋ฆฌ",
                "position": 6
            },
            {
                "name": "ํ† ๋ฏธ",
                "position": 4
            },
            {
                "name": "๋ธŒ๋ผ์šด",
                "position": 3
            },
        ]
    },
    {
        "winners": "๋ธŒ๋ฆฌ,ํ† ๋ฏธ,๋ธŒ๋ผ์šด",
        "racingCars": [
            {
                "name": "๋ธŒ๋ฆฌ",
                "position": 6
            },
            {
                "name": "ํ† ๋ฏธ",
                "position": 6
            },
            {
                "name": "๋ธŒ๋ผ์šด",
                "position": 6
            },
        ]
    }
]

์ด๋ฒˆ์—๋Š” ๋งŽ์€ ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆˆ ๊ฒŒ ์•„๋‹ˆ์—ˆ์–ด์„œ, ์•ž์œผ๋กœ ๊ณ ๋ฏผํ•  ๋ถ€๋ถ„๊ณผ ๊ณ ๋ฏผํ–ˆ๋˜ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

 

๐Ÿ’ฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด alert ํ•ด์ฃผ๊ธฐ

์‚ฌ์‹ค ์ด๊ฑฐ๋Š” html ๊ด€๋ จ์ด์ง€๋งŒ, ๋‘๋‘ ์ด๋ž‘ ์ด๊ฑฐ ํ•˜๋ ค๊ณ  30๋ถ„ ์ •๋„๋Š” ์‚ฝ์งˆํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์•„์„œ ์ ์–ด๋‘”๋‹ค.

๊ธฐ์กด์˜ html ์ŠคํŽ™์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ๋‚ด๋ถ€ ์ฝ˜์†”์—์„œ๋งŒ 400 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์•„๋ฌด๋Ÿฐ ์ผ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค.

ํ•˜์ง€๋งŒ ๋ญ”๊ฐ€ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ญ”๊ฐ€ ๋‚˜ํƒ€๋‚ฌ์œผ๋ฉด ์ข‹๊ฒ ์–ด์„œ, alert ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

์ด๋Ÿฐ ์‹์œผ๋กœ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์•Œ๋ฆผ์ด ๊ฐ„๋‹ค.

fetch("/plays", {
            method: "POST",
            headers: {
                "Content-Type": "application/json;charset=UTF-8"
            },
            body: JSON.stringify(data)
        })
            ...
            .catch(error => {
                window.alert(JSON.parse(error.message).message);
                console.error("Error:", error);
            });

๊ทธ๋ƒฅ ์•„๋ž˜์ฒ˜๋Ÿผ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๊บผ๋‚ธ ๋‹ค์Œ์— (JSON.parse๋ฅผ ํ†ตํ•ด json ํ˜•ํƒœ๋กœ ๋‚ด๋ ค์˜จ ๊ฐ’์—์„œ ๊บผ๋‚ด๋Š” ๊ฒƒ) alert ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 


๐Ÿ’ฌ ๋„๋ฉ”์ธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ๋ทฐ์— ๋…ธ์ถœ์‹œ์ผœ๋„ ๋˜๋Š”๊ฐ€?

๋ ˆ๋ฒจ 1 ๋ฏธ์…˜์—์„œ๋Š” '์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœ์ƒํ•œ ์ง€์ ์—์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค' ๋ผ๋Š” ๊ฒƒ์— ์ดˆ์ ์„ ๋งž์ถ”๋‹ค ๋ณด๋‹ˆ, ์ฝ˜์†”์˜ ๋ทฐ์— ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜์–ด๋„ ๊ทธ๋ ‡๊ฒŒ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•˜๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ์›น์ธ ์ƒํ™ฉ์ด๊ณ  html์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋„๋ฉ”์ธ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ด์ง„๋‹ค.

๋งŒ์•ฝ ๋‹ค๊ตญ์–ด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด? ํ˜น์€, view์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ฌธ๊ตฌ๋ฅผ ์ถœ๋ ฅํ•ด์•ผ ํ•œ๋‹ค๋ฉด?

๋„๋ฉ”์ธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋‹ค๋ฅธ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ด๋‹ค.

 

๊ธฐ์กด์˜ ์ฝ”๋“œ์ด๋‹ค.

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ExceptionDto> handleIllegalArgumentException(final IllegalArgumentException exception) {
    final ExceptionDto exceptionDto = new ExceptionDto(exception.getMessage());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionDto);
}

๋„๋ฉ”์ธ์—์„œ ๋ฐœ์ƒ๋˜๋Š” IllegalArgumentException์— ๋‹ด๊ธด message๋ฅผ ๊บผ๋‚ด์™€์„œ response body๋กœ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ฐ ๋„๋ฉ”์ธ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œ์ผฐ์—ˆ๋‹ค.

private void validateDuplicateCarName() {
    int uniqueCarCount = new HashSet<>(cars).size();
    if (cars.size() != uniqueCarCount) {
        throw new DuplicateException(DUPLICATE_MESSAGE);
    }
}

 

์ด๊ฑธ ์–ด๋–ค ์‹์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ๊ณ ์•ˆํ–ˆ๋‹ค.

1. ๋„๋ฉ”์ธ๋งˆ๋‹ค ๊ฐ๊ฐ ๋‹ค๋ฅธ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์„œ ExceptionHandler์—์„œ ์˜ˆ์™ธ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ.
2. ์˜ˆ์™ธ๋Š” ๋™์ผํ•˜๊ฒŒ ๋‘๋˜, enum์„ ํ†ตํ•œ ErrorCode๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๊ธฐ

์ฒ˜์Œ์—๋Š” 2๋ฒˆ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ• ๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€, ErrorCode์˜ ๊ฒฝ์šฐ 400, 404 ์™ธ์˜ ์ปค์Šคํ…€ ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ณ  (๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ), ํ˜„์žฌ๋Š” ์‚ฌ์šฉ์ž์˜ ์ž˜๋ชป๋œ ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ ์ฝ”๋“œ ์ชฝ๊ณผ๋Š” ๊ด€๋ จ์žˆ๊ฒŒ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•˜๋‹ค.

๋ฏธ์…˜์ด ์ข…๋ฃŒ๋œ ์ง€๊ธˆ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ๊ทธ๋ƒฅ ErrorCode๋ผ๋Š” ์ด๋ฆ„ ๋Œ€์‹ ์— ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ enum์œผ๋กœ ๋‘๊ณ , ๋‚ด๋ถ€์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌํ•ด์„œ ExceptionHandler์—์„œ๋Š” ํ•ด๋‹น ๊ฐ์ฒด์— ๋‹ด๊ธด ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋„๋ก ํ•ด๋„ ๋˜์ง€ ์•Š์•˜์„๊นŒ ์‹ถ๋‹ค.

 

์šฐ์„ , ๊ธฐ๋ณธ์ ์œผ๋กœ ๋„˜์–ด์˜จ ๊ฐ’์ด null์ด๊ฑฐ๋‚˜ ๋น„์–ด์žˆ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์ฒดํฌ๋Š” Request DTO์—๊ฒŒ ์ฑ…์ž„์„ ๋„˜๊ฒผ๋‹ค.

public class RaceRequest {

    @NotBlank(message = "์ž๋™์ฐจ ์ด๋ฆ„์€ ๋น„์–ด์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    private final String names;

    @NotNull(message = "๊ฒฝ์ฃผ ํšŸ์ˆ˜๋Š” ๋น„์–ด์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    private final int count;

    ...
}

DTO๋Š” ๋ทฐ์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ์—ญํ• ์ด๊ธฐ ๋•Œ๋ฌธ์— message๋ฅผ ๊ด€๋ฆฌํ•ด๋„ ์ƒ๊ด€์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜์˜€๊ณ ,

๋„๋ฉ”์ธ์—์„œ๋Š” ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์™„์ „ํ•œ ๊ฐ’์„ ๋ฐ›๋Š” ๊ฒƒ์ด ๋งž๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— validation annotation์„ ํ†ตํ•˜์—ฌ ๊ด€๋ฆฌํ–ˆ๋‹ค.

 

๊ทธ์™ธ์˜ ์ปค์Šคํ…€ ์˜ˆ์™ธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง„ํ–‰ํ•˜์˜€๋‹ค.

public class ErrorData<T> {
    private final T data;

    public ErrorData(final T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

์šฐ์„ , ErrorData๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋Š” ๋„๋ฉ”์ธ๋ณ„๋กœ ๋‹ค๋ฅธ ์ œํ•œ ์กฐ๊ฑด์„ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€์— ์œ ๋™์ ์œผ๋กœ ๋‹ด๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋‹ค.

private void validateRange(final int raceCount) {
    if (raceCount <= MIN_RACE_COUNT) {
        throw new ResourceMinRangeException(new ErrorData<>(MIN_RACE_COUNT));
    }
}

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์™ธ๋ฅผ ๊ฒ€์ฆํ•˜์˜€๋‹ค. ์ด๋Š” ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด์˜จ ๊ฒฝ์ฃผ ํšŸ์ˆ˜๊ฐ€ ์ตœ์†Œ ํšŸ์ˆ˜ ์ดํ•˜์ธ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.

์ธ์ž๋กœ ๋„๋ฉ”์ธ์˜ ๊ทœ์น™์ธ '0๋ฒˆ ์ดํ•˜'์˜ ๊ฐ’์„ ๋‹ด๋Š” MIN_RACE_COUNT๋ฅผ ๋‹ด์•˜๋‹ค.

 

๋ฌผ๋ก  @Range ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค.

1. raceCount๊ฐ€ 0 ์ดํ•˜๋ผ๋Š” ๊ฐ’์€ ๋„๋ฉ”์ธ ์ž์ฒด์˜ ๊ทœ์น™์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

(๋งŒ์•ฝ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋ณ€๊ฒฝ๋˜์–ด์„œ ๊ฒฝ์ฃผ ํšŸ์ˆ˜๋Š” ์ตœ์†Œ 5๋ฒˆ๋ถ€ํ„ฐ ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋ฉด?)

2. ๋„๋ฉ”์ธ์—์„œ @Range๋ฅผ ํ†ตํ•ด ์ธ์ž๋กœ message๋ฅผ ๋„˜๊ธฐ๋ฉด ๊ฒฐ๊ตญ ๋ทฐ์— ๋ณด์—ฌ์งˆ ๊ฐ’์„ ๋˜ ๋‹ค์‹œ ๋„๋ฉ”์ธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค.

 

๐ŸŒฑ ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ๋ฆฌ๋ทฐ์–ด๋‹˜์ด ์˜๊ฒฌ์„ ๋‚จ๊ฒจ์ฃผ์…จ์–ด์„œ ๋ฉ”๋ชจํ•ด๋‘๊ณ ์ž ํ•œ๋‹ค.

- Presentation Layer์—์„œ ๊ฒ€์ฆ๋˜์–ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๋„๋ฉ”์ธ์—์„œ ๊ฒ€์ฆ๋˜์–ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค.
๋„๋ฉ”์ธ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ ์šฉ๋˜์–ด์•ผ ํ•˜๋Š” ๊ณตํ†ต์ ์ธ '๋„๋ฉ”์ธ ๊ทœ์น™'์ด๊ณ , ํด๋ผ์ด์–ธํŠธ์— ๋”ฐ๋ผ์„œ ์ ์šฉ๋˜์–ด์•ผ ํ•˜๋Š” ๊ทœ์น™์€ ๋„๋ฉ”์ธ์— ์žˆ์œผ๋ฉด ์•ˆ ๋œ๋‹ค. (ํŠน์ •ํ•œ view๋ฅผ ์˜์กดํ•ด์„œ ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค)

๊ทธ๋ž˜์„œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์ž๋ฉด, '์ž๋™์ฐจ ์ด๋ฆ„์ด 10์ž ์ด๋‚ด์—ฌ์•ผ ํ•œ๋‹ค'๋ผ๋Š” ๊ทœ์น™์ด ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ ์šฉ๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด ๋„๋ฉ”์ธ์˜ ๊ทœ์น™์ด ๋  ๊ฒƒ์ด๊ณ , ์ฝ˜์†”์—์„œ๋Š” 10์ž, ์›น์—์„œ๋Š” 20์ž ๊ฐ™์ด ํด๋ผ์ด์–ธํŠธ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ๊ทœ์น™์ด๋ผ๋ฉด Presentation Layer์—์„œ ๊ฒ€์ฆํ•ด์•ผ ํ•˜๋Š” ๊ทœ์น™์ด ๋œ๋‹ค.

 

๋‹ค๋งŒ, ๊ฐ ๋„๋ฉ”์ธ๋งˆ๋‹ค ๋ชจ๋“  ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์˜ˆ์™ธ๋ฅผ ๋‹ด๋Š” ๊ฐ์ฒด๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์งˆ ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์„œ ์šฐ์„ ์€ ์ด๋ฆ„์„ ๋ฒ”์šฉ์ ์œผ๋กœ ์ง€์—ˆ๋‹ค. ์ด๊ฑฐ๋Š” ์ด์ „ ๋ฏธ์…˜์—์„œ ๋‹ค๋ฅธ ๋ฆฌ๋ทฐ์–ด๋‹˜๊ณผ๋„ ์–˜๊ธฐ๋ฅผ ๋‚˜๋ˆ ๋ณธ ์ ์ด ์žˆ์—ˆ์–ด์„œ, ์‚ฌ์‹ค ๊ฝค ๊ณ ๋ฏผ์ด ๋˜๋Š” ๋ถ€๋ถ„์ด์—ˆ๋Š”๋ฐ ์šฐ์„ ์€ ErrorCode๋ฅผ ์ œ๋„ค๋ฆญํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๊ณ , ๋ฐœ์ƒ๋˜๋Š” ์ด์œ ๊ฐ€ ๋ณ„๋กœ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ ‡๊ฒŒ ํ•ด๋„ ํ•œ ์˜ˆ์™ธ๊ฐ€ ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด์„œ ๊ดœ์ฐฎ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

public class ResourceMinRangeException extends RuntimeException {

    private final ErrorData<Integer> range;

    public ResourceMinRangeException(final ErrorData<Integer> range) {
        this.range = range;
    }

    public ErrorData<Integer> getRange() {
        return range;
    }
}

์˜ˆ์™ธ ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค์—ˆ๋‹ค. ์ž์›์ด ์ตœ์†Œ ๋ฒ”์œ„ ๋ฏธ๋งŒ์˜ ๊ฐ’์„ ๊ฐ€์กŒ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ผ๋Š” ์˜๋ฏธ๋กœ ๋‹ด์•˜๋‹ค.

๊ทธ๋ฆฌ๊ณ , ํ•ด๋‹น ๋ฒ”์œ„๋ฅผ ๋‚ด๋ถ€์—์„œ ์ธ์ž๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก (ErrorData<Integer>) ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ @ExceptionHandler๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์˜€๋‹ค.

@ExceptionHandler(ResourceMinRangeException.class)
public ResponseEntity<ExceptionResponse> resourceRangeException(final ResourceMinRangeException e) {
    final Integer minRange = e.getRange().getData();
    final ExceptionResponse exceptionResponse =
            new ExceptionResponse(String.format("%d๋ณด๋‹ค ํฐ ๊ฐ’์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.", minRange));
    return ResponseEntity.badRequest().body(exceptionResponse);
}

๋‚ด๊ฐ€ ๋งŒ์•ฝ ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž์™€ ํ˜‘์—…์„ ํ•˜๋Š” ์ƒํ™ฉ์ด๊ณ , ๋ทฐ์— ๋‚ด๋ ค์งˆ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•ด ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์–ด๋–ค ํด๋ž˜์Šค๋ฅผ ํ™•์ธํ• ๊นŒ? ๋ผ๋Š” ๊ด€์ ์œผ๋กœ ๊ณ ๋ฏผํ•ด๋ดค๋Š”๋ฐ, ๋‚˜๋ผ๋ฉด ์˜ˆ์™ธ์— ๋Œ€ํ•ด ํ•ธ๋“ค๋งํ•˜๋Š” ControllerAdvice ๋ถ€๋ถ„์„ ๋จผ์ € ํ™•์ธํ•  ๊ฒƒ ๊ฐ™์•„์„œ ์œ„์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•˜์˜€๋‹ค.

๋˜ํ•œ, ์˜ˆ์™ธ response๋ฅผ ๋‹ด๋Š” ๊ฐ์ฒด๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์—ˆ์—ˆ๋Š”๋ฐ, ๋‚˜์ค‘์— ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž์™€ ํ˜‘์—…ํ–ˆ์„ ๋•Œ json์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๋กœ string์ด ๋‚ด๋ ค์˜ค๋Š” ๊ฒƒ๋ณด๋‹ค, ๊ณตํ†ต๋œ ํฌ๋งท์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ•ด๋‘๊ณ  ๋‚ด๋ ค์ฃผ๋Š” ๊ฒŒ ๋‚˜์„ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. ์ง€๊ธˆ์€ ๋ฉ”์‹œ์ง€์ผ ๋ฟ์ด์ง€๋งŒ ๋‚˜์ค‘์— ์ƒํƒœ ์ฝ”๋“œ๋„ ํ•จ๊ป˜ ๋‚ด๋ ค์˜จ๋‹ค๋“ ์ง€... ๊ทธ๋Ÿด ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ๋Š” ๊ฒŒ ๋” ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ธฐ๋„ ํ–ˆ๋‹ค.

 


 ๐Ÿ’ฌ Dao์™€ Repository

๋‚˜๋Š” ์ด๋ฒˆ ๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ•˜์˜€๋‹ค.

์œ„๋Š” ๊ฒฝ์ฃผ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ”์ด๊ณ , ๊ฒฝ์ฃผ ํšŸ์ˆ˜์™€ ์šฐ์Šน์ž, ๊ทธ๋ฆฌ๊ณ  ๊ฒฝ์ฃผ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ๋‹ด์•˜๋‹ค.

์•„๋ž˜๋Š” ์ž๋™์ฐจ์— ๋Œ€ํ•ด ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ”์ด๊ณ , ์ž๋™์ฐจ ์ด๋ฆ„๊ณผ ์œ„์น˜, ๊ทธ๋ฆฌ๊ณ  ์™ธ๋ž˜ํ‚ค๋กœ ๊ฒฝ์ฃผ ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์•„์ด๋””๋ฅผ ๊ฐ€์กŒ๋‹ค.

๋˜ํ•œ, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ƒ์—์„œ ํ•˜๋‚˜์˜ ๊ฒฝ์ฃผ์—๋Š” ์ค‘๋ณต๋œ ์ž๋™์ฐจ ์ด๋ฆ„์„ ๊ฐ€์งˆ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— UNIQUE๋ฅผ ๊ฑธ์–ด์ฃผ์—ˆ๋‹ค.

@Repository
@Transactional(readOnly = true)
public class CarRaceRepositoryImpl implements CarRaceRepository {

    private final CarDao carDao;
    private final RaceResultDao raceResultDao;
	...
}

๊ทธ๋ž˜์„œ, ๋ณธ ์š”๊ตฌ์‚ฌํ•ญ์—์„œ ์ •๋ณด๋ฅผ ์ €์žฅํ•  ๋•Œ ํ•˜๋‚˜์˜ Repository์—์„œ ์ €์žฅํ•˜๋Š” ๋‘ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์ด 2๊ฐœ์˜ Dao๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ˜•์‹์œผ๋กœ ์ง„ํ–‰ํ–ˆ์—ˆ๋‹ค. ๋˜ํ•œ, ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ ํ•ด๋‹น ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฆฌ๋ทฐ๊ฐ€ ๋‹ฌ๋ ธ์—ˆ๋‹ค.

๋‘ Dao๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š”๊ฒƒ์ด ๋‹จ์ผ์ฑ…์ž„์›์น™์„ ์œ„๋ฐฐํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋Š” ์•Š์œผ์‹ ๊ฐ€์š”?

๋‚˜๋Š” ์ด์— ๋Œ€ํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹ต๋ณ€์„ ๋‹ฌ์•˜์—ˆ๋‹ค.

DAO์˜ ๊ฒฝ์šฐ 1:1 ๋งคํ•‘์ด ๋˜์ง€๋งŒ, Repository์˜ ๊ฒฝ์šฐ Dao๋ฅผ ํ•ฉ์ณ ์กฐ๊ธˆ ๋” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ด€์ ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ณ„์ธต์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

๋งŒ์•ฝ ์‚ฌ์šฉ์ž์˜ ํŠธ์œ— ๋ชฉ๋ก์„ ๊ฐ€์ง€๊ณ  ์˜ค๊ฒŒ ๋œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‹ด์€ Dao์™€ ํŠธ์œ—์— ๋Œ€ํ•œ Dao๋ฅผ ํ•ฉ์ณ์„œ ํ•ด๋‹น Repository์—์„œ ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. (๋‹น์‹œ ์ด ๊ธ€์„ ๋ณด๊ณ  ์ด๋Ÿฌํ•œ ์ƒ๊ฐ์„ ์žก์•˜์—ˆ๋‹ค.)

 

ํ•˜์ง€๋งŒ, ๋ฆฌ๋ทฐ์–ด๋‹˜์€ ๋‚˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ๊ณ  ๋ง์”€ํ•ด ์ฃผ์…จ๋‹ค.

๋‹น์‹œ ์Šฌ๋ž™ ํ—ˆ๋“ค์„ ํ†ตํ•ด ๋ง์„ ๋‚˜๋ˆด๋˜์ง€๋ผ ๋”ฐ๋กœ ๊ธฐ๋ก์€ ๋ชปํ–ˆ๋Š”๋ฐ, ๋‚ด๊ฐ€ ์ดํ•ดํ•œ ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

์ง€๊ธˆ ๋‚˜์˜ ๊ฒฝ์šฐ, ์ž๋™์ฐจ์™€ ๊ฒฝ์ฃผ ๊ฒŒ์ž„์€ FK๋กœ ์—ฐ๊ฒฐ์€ ๋˜์–ด ์žˆ์ง€๋งŒ ๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์œผ๋กœ ์—ฐ๊ด€์ด ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.

์˜ค์ง 'Car'๋ผ๋Š” ๋„๋ฉ”์ธ๊ณผ, 'Race'๋ผ๋Š” ๋„๋ฉ”์ธ๋งŒ ์กด์žฌํ•  ๋ฟ์ด์ง€, ๋‘ ๊ฐ€์ง€์— ๋Œ€ํ•ด ์ง‘ํ•ฉ๋œ ๋„๋ฉ”์ธ์€ ์กด์žฌํ•˜์ง€ ์•Š์•˜๋‹ค.

 

ํ•˜์ง€๋งŒ, ์œ„์˜ ๋ ˆํผ๋Ÿฐ์Šค์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค.

public class UserRepositoryImpl implements UserRepository {
    private UserDaoImpl userDaoImpl;
    private TweetDaoImpl tweetDaoImpl;
    
    @Override
    public User get(Long id) {
        UserSocialMedia user = (UserSocialMedia) userDaoImpl.read(id);
        
        List<Tweet> tweets = tweetDaoImpl.fetchTweets(user.getEmail());
        user.setTweets(tweets);
        
        return user;
    }
}

์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ๋ถ€๋ถ„์€ userDaoImpl.read์˜ ๋ฐ˜ํ™˜์ฒด๊ฐ€ UserSocialMedia๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

Our UserSocialMedia class is a complex domain containing the properties of the User domain too.

์‚ฌ์šฉ์ž ๋„๋ฉ”์ธ์˜ ์†์„ฑ๊ณผ ํŠธ์œ—์— ๋Œ€ํ•œ ์ •๋ณด๊นŒ์ง€ ๋‹ด๊ณ  ์žˆ๋Š”, ํ•˜๋‚˜๋กœ ํฌ๊ด„ํ•œ ๋„๋ฉ”์ธ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ DB์˜ ์ •๊ทœํ™” ๊ด€์ ์—์„œ ๋ถ„๋ฆฌ๋œ ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ ๋„๋ฉ”์ธ์œผ๋กœ ์ง‘์•ฝํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

 

๐ŸŒฑ DAO

- Data persistence์˜ ์ถ”์ƒํ™” (Database์— ๋Œ€ํ•œ ์ถ”์ƒํ™” ๊ณ„์ธต)

- Database์™€ ์ธ์ ‘ํ•œ ๊ณ„์ธต์ด๋ฉฐ, ํ•˜์œ„ ์ˆ˜์ค€์˜ ๊ฐœ๋…์ด๋‹ค.

- ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋งคํ•‘ ๋ฐ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค

 

๐ŸŒฑ Repository

- ๊ฐ์ฒด์˜ ์ง‘ํ•ฉ์— ๋Œ€ํ•œ ์ถ”์ƒํ™”

- ๋„๋ฉ”์ธ ์˜ค๋ธŒ์ ํŠธ์— ์กฐ๊ธˆ ๋” ๊ฐ€๊นŒ์šด ๊ฐœ๋…์ด๋‹ค.

- ๋„๋ฉ”์ธ์™€ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต ์‚ฌ์ด์— ์กด์žฌํ•˜๋Š” ๋ ˆ์ด์–ด์ด๋‹ค.

(๋ฌผ๋ก , ๋„๋ฉ”์ธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๊ฑฐ์˜ ์—†๋‹ค๋ฉด Repository๊ฐ€ ๊ณง ๋‹จ์ˆœํ•œ Dao ํ˜•ํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.)

 


์‚ฌ์‹ค ๊ทธ ์™ธ๋„ ๊ฝค๋‚˜ ๋Š๊ผˆ๋˜ ์ ์ด ๋งŽ์€๋ฐ, ์ด๊ฑฐ๋Š” ํšŒ๊ณ ๋กœ ์“ฐ๋Š” ๊ฒƒ๋ณด๋‹ค ๋ณ„๋„์˜ ๊ธ€๋กœ ์“ฐ๋Š” ๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ๋‚จ๊ฒจ๋‘”๋‹ค.

๋ฏธ์…˜ ์ข…๋ฃŒ ๊ธฐ๊ฐ„๊นŒ์ง€ ์ตœ๋Œ€ํ•œ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ์“ฐ๋Š”๋ฐ์— ์ง‘์ค‘ํ•ด์•ผ๊ฒ ๋‹ค!

 

Comments