CHAP06 - DDD-START/ONLINE-STUDY GitHub Wiki

  • ์‘์šฉ์„œ๋น„์Šค ๊ตฌํ˜„
  • ํ‘œํ˜„ ์˜์—ญ์˜ ์—ญํ™œ
  • ๊ฐ’ ๊ฒ€์ฆ๊ณผ ๊ถŒํ•œ ๊ฐ์‚ฌ

ํ‘œํ˜„ ์˜์—ญ๊ณผ ์‘์šฉ์˜์—ญ

๋„๋ฉ”์ธ ์˜์—ญ์„ ์ž˜ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜์ง€๋งŒ ๋„๋ฉ”์ธ์ด ์ œ ๊ธฐ๋Šฅ์„ ํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž์™€ ๋„๋ฉ”์ธ์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๋งค๊ฐœ์ฒด(์‘์šฉ์˜์—ญ)๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

image

  • ํ‘œํ˜„ ์˜์—ญ
    • ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ํ•ด์„
    • HTTP ์š”์ฒญ์˜ URL, ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ, ์ฟ ํ‚ค, ํ—ค๋” ๋“ฑ์œผ๋กœ ์•Œ๋งž๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์‘์šฉ ์„œ๋น„์Šค ์‹คํ–‰ (์‘์šฉ ์„œ๋น„์Šค๊ฐ€ ์š”๊ตฌํ•˜๋Š” ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ๋ณ€ํ™˜), ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ฆฌํ„ด
  • ์‘์šฉ ์˜์—ญ
    • ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณต
    • ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ž…๋ ฅ๊ฐ’์„ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ด
public ModelAndView join(HttpServletRequest request) {

    String email = request.getParameter("email");
    String password = request.getParameter("password");
    // ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ์‘์šฉ ์„œ๋น„์Šค์— ๋งž๊ฒŒ ๋ณ€ํ™˜
    JoinRequest joinReq = new JoinRequest(email, password);
    // ๋ณ€ํ™˜ํ•œ ๊ฐ์ฒด(๋ฐ์ดํ„ฐ)๋ฅผ ์ด์šฉํ•ด์„œ ์‘์šฉ ์„œ๋น„์Šค ์‹คํ–‰
    joinService.join(joinReq);
    // ...
}

์›น๋ธŒ๋ผ์šฐ์ € > HTML ์‘๋‹ต

REST ํด๋ผ์ด์–ธํŠธ > JSON ์ด๋‚˜ XML ํ˜•์‹

์‚ฌ์šฉ์ž์™€์˜ ์ƒํ˜ธ ์ž‘์šฉ์€ ํ‘œํ˜„ ์˜์—ญ์ด ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ์„œ๋น„์Šค๋Š” ํ‘œํ˜„ ์˜์—ญ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค
์‘์šฉ ์˜์—ญ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ง€, Rest API ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ง€, TCP ์†Œ์ผ“์„ ์‚ฌ์šฉํ•˜๋Š” ์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†๋‹ค.

์‘์šฉ ์„œ๋น„์Šค์˜ ์—ญํ• 

์‘์šฉ ์„œ๋น„์Šค๋Š” ์‚ฌ์šฉ์ž(ํด๋ผ์ด์–ธํŠธ)๊ฐ€ ์š”์ฒญํ•œ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๊ณ , ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋กœ๋ถ€ํ„ฐ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ๊ตฌํ•˜๊ณ , ๋„๋ฉ”์ธ ๊ฐ์ฒด ์‚ฌ์šฉ (๋„๋ฉ”์ธ ์˜์—ญ๊ณผ ํ‘œํ˜„ ์˜์—ญ์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์ฐฝ๊ตฌ์ธ ํผ์‚ฌ๋“œ ์—ญํ™œ)

๋„๋ฉ”์ธ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๋Š” ์‘์šฉ ์„œ๋น„์Šค

public Result doSomeFunc(SomeReq req) {
    // 1. ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์—์„œ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ๊ตฌํ•œ๋‹ค.
    SomeAgg agg = someAggRepository.findById(req.getId());
    checkNull(agg);
    // 2. ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์˜ ๋„๋ฉ”์ธ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค.
    agg.doFunc(req.getValue());
    // 3. ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
    return createSuccessResult(agg);
    // ...
}

์ƒˆ๋กœ์šด ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์‘์šฉ ์„œ๋น„์Šค

public Result doSomeCreation(CreateSomeReq req) {
    // 1. ๋ฐ์ดํ„ฐ ์ค‘๋ณต ๋“ฑ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.
    checkValid(req);
    // 2. ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    SomeAgg newAgg = createSome(req);
    // 3. ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์— ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ์ €์žฅํ•œ๋‹ค.
    someAggRepository.save(newAgg);
    // 4. ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
    return createSuccessResult(newAgg);
    // ...
}

์‘์šฉ ์„œ๋น„์Šค์˜ ์ฃผ๋œ ์—ญํ• 

  • ๋„๋ฉ”์ธ ๊ฐ์ฒด ๊ฐ„์˜ ์‹คํ–‰ ํ๋ฆ„ ์ œ์–ด
  • ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ (์‘์šฉ ์„œ๋น„์Šค๋Š” ๋„๋ฉ”์ธ์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.)
  • ์ ‘๊ทผ์ œ์–ด
  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

๋„๋ฉ”์ธ ๋กœ์ง์€ ๋„๋ฉ”์ธ ์˜์—ญ์— ์œ„์น˜ํ•˜๊ณ  ์‘์šฉ ์„œ๋น„์Šค๋Š” ๋„๋ฉ”์ธ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์˜ˆ ) ์•”ํ˜ธ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ์„ ์œ„ํ•œ ์‘์šฉ ์„œ๋น„์Šค๋Š” Member ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์™€ ๊ด€๋ จ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ ๋„๋ฉ”์ธ ๊ฐ์ฒด ๊ฐ„์˜ ์‹คํ–‰ ํ๋ฆ„์„ ์ œ์–ดํ•œ๋‹ค.

public class ChangePasswordService {
    public void changePassword(String memberId, String oldPw, String newPw) {
        Member member = memberRepository.findById(memberId);
        checkMember(member);
        member.changePassword(oldPw, newPw);
    }
}

์•”ํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด ๊ฒ€์ฆํ•˜๋Š” ๋กœ์ง์€ Member ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์—!

public class Member {
    public void changePassword(String oldPw, String newPw) {
        if (!matchPassword(oldPw)) {
            throw new BadPasswordException();
        }
        setpassword(newPw);
    }

    // ํ˜„์žฌ ์•”ํ˜ธ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๋„๋ฉ”์ธ ๋กœ์ง
    public boolean matchPassword(String pwd) {
        return passwordEncoder.matches(pwd);
    }

    private void setPassword(String newpw) {
        if (isEmpty(newPw)) {
            throw new IllegalArgumentException("no new password");
        }
        this.password = newPw;
    }
}

๋„๋ฉ”์ธ ๋กœ์ง์„ ๋„๋ฉ”์ธ ์˜์—ญ๊ณผ ์‘์šฉ ์„œ๋น„์Šค์— ๋ถ„์‚ฐํ•ด์„œ ๊ตฌํ˜„ํ•˜๋ฉด..

  • ์ฝ”๋“œ์˜ ์‘์ง‘์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
    • ๋„๋ฉ”์ธ ๋ฐ์ดํ„ฐ์™€ ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋„๋ฉ”์ธ ๋กœ์ง์ด ํ•œ ์˜์—ญ์— ์œ„์น˜ํ•˜์ง€ ์•Š๊ณ , ์„œ๋กœ ๋‹ค๋ฅธ ์˜์—ญ์— ์œ„์น˜ํ•˜๊ฒŒ ๋จ
    • ๋„๋ฉ”์ธ ๋กœ์ง์„ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์˜์—ญ์„ ๋ถ„์„ํ•ด์•ผ
  • ์—ฌ๋Ÿฌ ์‘์šฉ ์„œ๋น„์Šค์—์„œ ๋™์ผํ•œ ๋กœ์ง์„ ์ด์šฉํ•˜๊ณ ์ž ํ•  ๊ฒฝ์šฐ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ณ€๊ฒฝ์ด ์–ด๋ ค์›Œ์ง

์‘์šฉ ์„œ๋น„์Šค์˜ ๊ตฌํ˜„

์‘์šฉ ์„œ๋น„์Šค > ํ‘œํ˜„ ์˜์—ญ๊ณผ ๋„๋ฉ”์ธ ์˜์—ญ์„ ์—ฐ๊ฒฐํ•˜๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• . (๋””์ž์ธํŒจํ„ด์˜ ํผ์‚ฌ๋“œ ํŒจํ„ด๊ณผ ๊ฐ™์€ ์—ญํ• )

์‘์šฉ ์„œ๋น„์Šค ์ž์ฒด์— ๋ณต์žกํ•œ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ์„œ๋น„์Šค์˜ ๊ตฌํ˜„์€ ์–ด๋ ต์ง€ ์•Š๋‹ค.

์‘์šฉ ์„œ๋น„์Šค์˜ ํฌ๊ธฐ

  • ํ•œ ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค์— ํšŒ์› ๋„๋ฉ”์ธ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
  • ๊ตฌ๋ถ„๋˜๋Š” ๊ธฐ๋Šฅ ๋ณ„๋กœ ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ

ํด๋ž˜์Šค์˜ ํฌํ‚ค๊ฐ€ ์ปค์ง vs ํด๋ž˜์Šค ๊ฐœ์ˆ˜ ์ฆ๊ฐ€

  • ๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ, ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ์ค‘๋ณตํ•ด์„œ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋˜์–ด ์ฝ”๋“œ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•œ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ ์—ญํ• ์„ ๊ฐ–๋Š” ๊ฒƒ๋ณด๋‹ค ๊ฐ ํด๋ž˜์Šค๋งˆ๋‹ค ๊ตฌ๋ถ„๋˜๋Š” ์—ญํ• ์„ ๊ฐ–๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.

์‘์šฉ ์„œ๋น„์Šค์˜ ์ธํ„ฐํŽ˜์ด์Šค์™€ ํด๋ž˜์Šค

์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ

  • ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๊ฒฝ์šฐ๋‚˜ ๋Ÿฐํƒ€์ž„์— ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๊ต์ฒดํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ ( ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์€ ๋งค์šฐ ๋“œ๋ฌผ๋‹ค.)

๋ฐœ์ƒ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ

  • ์†Œ์Šค ํŒŒ์ผ์˜ ์ฆ๊ฐ€
  • ๊ตฌํ˜„ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๊ฐ„์ ‘ ์ฐธ์กฐ ์ฆ๊ฐ€ > ์ „์ฒด ๊ตฌ์กฐ๊ฐ€ ๋ณต์žกํ•ด ์ง
    TDD๋ฅผ ์ฆ๊ฒจํ•˜๊ณ  ํ‘œํ˜„ ์˜์—ญ๋ถ€ํ„ฐ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•œ๋‹ค๋ฉด ๋ฏธ๋ฆฌ ์‘์šฉ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์–ด ์‘์šฉ ์„œ๋น„์Šค์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ถ€ํ„ฐ ์ž‘์„ฑํ•˜๊ฒŒ ๋ ํ…๋ฐ, TDD ๋กœ ๋จผ์ € ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฐœ๋ฐœํ•œ๋‹ค๋ฉด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉํ•  ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค์˜ ๊ตฌํ˜„์€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์‘์šฉ ์„œ๋น„์Šค์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•ด์„œ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๊ตฌํ˜„์„ ์™„์„ฑํ•ด๋‚˜๊ฐ€๊ฒŒ ๋œ๋‹ค

Mocjito ์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋Š” ํด๋ž˜์Šค์— ๋Œ€ํ•ด์„œ๋„ ํ…Œ์ŠคํŠธ์šฉ ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†์–ด๋„ ํ‘œํ˜„์˜์—ญ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
๋”ฐ๋ผ์„œ, ์‘์šฉ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ํ•„์š”์„ฑ X

๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๊ฐ’ ๋ฆฌํ„ด

์‘์šฉ ์„œ๋น„์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋„๋ฉ”์ธ์„ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์š”๊ตฌํ•œ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ฐ’์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.

  • ํ•„์š”ํ•œ ๊ฐ’์„ ๊ฐœ๋ณ„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๊ธฐ
  • ๊ฐ์ฒด๋กœ ์ „๋‹ฌ ๋ฐ›๊ธฐ (ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋‘๊ฐœ ์ด์ƒ ์กด์žฌํ•  ๊ฒฝ์šฐ)
@Controller
@RequestMapping("/member/changePassword")
public class MemberPasswordController {

    @Autowired
    private ChangePasswordService changePasswordService;

    // ์ž๋ฐ” ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ ์‘์šฉ ์„œ๋น„์Šค์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด
    // ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๊ธฐ์— ์ข‹์Œ
    @RequestMapping(method = RequestMethod.POST)
    public String submit(ChangePasswordRequest changePwdReq) {
        Authentication auth = SecurityContext.getAuthentication();
        changePwdReq.setMemberId(auth.getid());
        try {
            changePasswordService.changePassword(changePwdReq);
        } catch (NoMemberException ex) {
            // ์•Œ๋งž์€ ์ต์…‰์…˜ ์ฒ˜๋ฆฌ ๋ฐ ์‘๋‹ต
        }
    }
}

ํ‘œํ˜„ ์˜์—ญ์—์„œ ์‘์šฉ ์„œ๋น„์Šค์˜ ๊ฒฐ๊ณผ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์‘์šฉ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฆฌํ„ด.
์˜ˆ ) ์ฃผ๋ฌธ > ์ฃผ๋ฌธ๋ฒˆํ˜ธ ๋ฆฌํ„ด

public class OrderService {
    @Transactional
    public OrderNo placeOrder(OrderRequest orderRequest) {
        OrderNo orderNo = orderRepository.nextId();
        Order order = createOrder(orderNo, orderRequest);
        orderRepository.save(order);
        // ์‘์šฉ ์„œ๋น„์Šค ์‹คํ–‰ ํ›„ ํ‘œํ˜„ ์˜์—ญ์—์„œ ํ•„์š”ํ•œ ๊ฐ’ ๋ฆฌํ„ด
        return orderNo;
    }
}

์‘์šฉ ์„œ๋น„์Šค์—์„œ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ์ž์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋„๋ฉ”์ธ ๋กœ์ง ์‹คํ–‰์„ ์„œ๋น„์Šค์™€ ํ‘œํ˜„ ์˜์—ญ ๋‘ ๊ณณ์—์„œ -> ๋กœ์ง์„ ์‘์šฉ ์„œ๋น„์Šค์™€ ํ‘œํ˜„ ์˜์—ญ์— ๋ถ„์‚ฐ -> ์ฝ”๋“œ์˜ ์‘์ง‘๋„ ๋‚ฎ์•„์ง

์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์‘์šฉ ์„œ๋น„์Šค๊ฐ€ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ๋ฆฌํ„ดํ•  ๊ฒฝ์šฐ ํ•ด๋‹น ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์˜ ๊ธฐ๋Šฅ์„ ์ปจํŠธ๋กค๋Ÿฌ๋‚˜ ๋ทฐ ์ฝ”๋“œ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๊ทœ์น™๋ณด๋‹ค๋Š” ์‘์šฉ ์„œ๋น„์Šค๋Š” ํ‘œํ˜„ ์˜์—ญ์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•˜์—ฌ ๊ธฐ๋Šฅ ์‹คํ–‰ ๋กœ์ง์˜ ์‘์ง‘๋„๋ฅผ ๋†’์ด์ž. ( ๋” ํ™•์‹คํ•œ ๋ฐฉ๋ฒ• ! )

ํ‘œํ˜„ ์˜์—ญ์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ

์˜ˆ ) HttpServletRequest / HttpSession ์„ ์‘์šฉ ์„œ๋น„์Šค์— ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š๋„๋ก

์‘์šฉ ์„œ๋น„์Šค์—์„œ ํ‘œํ˜„ ์˜์—ญ์— ๋Œ€ํ•œ ์˜์กด์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ

  • ์‘์šฉ์„œ๋น„์Šค๋งŒ ๋‹จ๋…์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง
  • ํ‘œํ˜„ ์˜์—ญ์˜ ๊ตฌํ˜„์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์‘์šฉ ์„œ๋น„์Šค์˜ ๊ตฌํ˜„๋„ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•จ ํ‘œํ˜„ ์˜์—ญ์˜ ์‘์ง‘๋„ ๊นจ์ง€๊ฒŒ ๋˜์–ด ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ ์ฆ๊ฐ€.. -> ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ ํ‘œํ˜„ ์˜์—ญ์˜ ๊ตฌํ˜„ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก

ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ

์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” @Transactional ์‚ฌ์šฉํ•˜๊ธฐ
Exception ๋ฐœ์ƒํ•˜๋ฉด, ๋กค๋ฐฑ / Exception ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด, ์ปค๋ฐ‹

๋„๋ฉ”์ธ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

์‘์šฉ ์„œ๋น„์Šค์—์„œ๋Š” ๋„๋ฉ”์ธ์—์„œ ๋ฐœ์ƒ์‹œํ‚จ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค
์ด๋ฒคํŠธ : ๋„๋ฉ”์ธ์—์„œ ๋ฐœ์ƒํ•œ ์ƒํƒœ ๋ณ€๊ฒฝ (์˜ˆ ) '์•”ํ˜ธ ๋ณ€๊ฒฝ๋จ' ์ด๋ฒคํŠธ, '์ฃผ๋ฌธ ์ทจ์†Œํ•จ' ์ด๋ฒคํŠธ)

public class Member {
    private Password password;
    
    public void initializePassword() {
        String newPassword = generateRandomPassword();
        this.password = new Password(newPassword);
        // ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ
        Events.raise(new PasswordChangedEvent(this.id, password));
    }
}

PasswordChangeEvent๋Š” ์•”ํ˜ธ๋ฅผ ์ดˆ๊ธฐํ™”ํ–ˆ์Œ์„ ํ‘œํ˜„ํ•˜๋Š” ์ด๋ฒคํŠธ

๋„๋ฉ”์ธ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ๊ทธ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ ์‘์šฉ ์„œ๋น„์Šค.
์‘์šฉ ์„œ๋น„์Šค๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์ด๋ฒคํŠธ์— ์•Œ๋งž์€ ํ›„์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹น ์˜ˆ ) ์•”ํ˜ธ ์ดˆ๊ธฐํ™”๋จ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ณ€๊ฒฝํ•œ ์•”ํ˜ธ๋ฅผ ์ด๋ฉ”์ผ๋กœ ๋ฐœ์†กํ•˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋“ฑ

public class InitPasswordService {
    @Transactional
    public void initializePassword(String memberId) {
        Events.handle((PasswordChangedEvent evt) -> {
            // evt.getId()์— ํ•ด๋‹นํ•˜๋Š” ํšŒ์›์—๊ฒŒ ์ด๋ฉ”์ผ ๋ฐœ์†กํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„
        });
        Member member = memberRepository.findById(memberId);
        checkMemberExists(member);
        member.initializePassword();
    }
}

member.initializePassword() -> PasswordChangedEvent() ์ด๋ฒคํŠธ ๋ฐœ์ƒ -> Events.handle() ์— ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ๋ฉ”์ผ์„ ๋ฐœ์†ก.

ํ‘œํ˜„ ์˜์—ญ

ํ‘œํ˜„ ์˜์—ญ์˜ ์ฑ…์ž„

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

image

๊ฐ’ ๊ฒ€์ฆ

  • ์‘์šฉ ์„œ๋น„์Šค ์˜์—ญ์—์„œ ์ฒ˜๋ฆฌ ํผ์— ์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด ์ง ๋ชจ๋“  ์กฐ๊ฑด์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์–ด๋ ค์›€ ( ์ฒซ๋ฒˆ์งธ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋งŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ)
  • ํ‘œํ˜„ ์˜์—ญ์—์„œ ์ฒ˜๋ฆฌ ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” Validator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฒ€์ฆ ๊ฐ€๋Šฅ ํ•„์ˆ˜ ๊ฐ’๊ณผ ๊ฐ’์˜ ํ˜•์‹์„ ๊ฒ€์‚ฌํ•˜๋ฉด ์‹ค์งˆ์ ์œผ๋กœ ์‘์šฉ ์„œ๋น„์Šค๋Š” ๋…ผ๋ฆฌ์  ์˜ค๋ฅ˜๋งŒ ๊ฒ€์‚ฌํ•˜๋ฉด ๋œ๋‹ค. ์ฆ‰, ๊ฐ™์€ ๊ฐ’ ๊ฒ€์‚ฌ๋ฅผ ํ‘œํ˜„ ์˜์—ญ๊ณผ ์‘์šฉ ์„œ๋น„์Šค์—์„œ ์ค‘๋ณต์œผ๋กœ ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง€๋Š” ๊ฒƒ, ๋”ฐ๋ผ์„œ ์‘์šฉ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ‘œํ˜„ ์˜์—ญ ์ฝ”๋“œ๊ฐ€ ํ•œ ๊ณณ์ด๋ฉด ๊ตฌํ˜„์˜ ํŽธ๋ฆฌํ•จ์„ ์œ„ํ•ด ์—ญํ• ์„ ๋‚˜๋ˆ„์–ด ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ํ‘œํ˜„์˜์—ญ: ํ•„์ˆ˜ ๊ฐ’, ๊ฐ’์˜ ํ˜•์‹, ๋ฒ”์œ„ ๋“ฑ์„ ๊ฒ€์ฆ
  • ์‘์šฉ ์„œ๋น„์Šค: ๋ฐ์ดํ„ฐ์˜ ์กด์žฌ ์œ ๋ฌด์™€ ๊ฐ’์€ ๋…ผ๋ฆฌ์  ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์ฆ
@Controller
public class Controller {
    @RequestMapping
    public String join(JoinRequest joinRequest, Errors errors) {
        new JoinRequestValidator().validate(joinRequest, errors);
        if (errors.hasErrors()) return formView;
        try {
            joinService.join(joinRequest);
            return successView;
        } catch (DuplicateIdException ex) {
            errors.rejectValue(ex.getPropertyName(), "duplicate");
            return formView;
        }
    }
}

๊ถŒํ•œ ๊ฒ€์‚ฌ

๊ฐœ๋… : ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ

๋‹จ์ˆœํ•œ ์‹œ์Šคํ…œ์€ ์ธ์ฆ ์—ฌ๋ถ€๋งŒ ๊ฒ€์‚ฌํ•˜๋ฉด ๋˜์ง€๋งŒ, ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€๋ฆฌ์ž์ธ์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์‹คํ–‰๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค. (์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋‚˜ ์•„ํŒŒ์น˜ Shiro ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ถŒํ•œ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ฐ€๋Šฅ)

๊ถŒํ•œ ๊ฒ€์‚ฌ ์˜์—ญ

  • ํ‘œํ˜„ ์˜์—ญ
  • ์‘์šฉ์„œ๋น„์Šค
  • ๋„๋ฉ”์ธ

image

URL ๋งŒ์œผ๋กœ ์ ‘๊ทผ ์ œ์–ด๋ฅผ ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ๋ฉ”์„œ๋“œ ๋‹จ์œ„๋กœ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ด์•ผ -> ์ด๊ฒƒ์ด ๊ผญ ์‘์šฉ ์„œ๋น„์Šค์˜ ์ฝ”๋“œ์—์„œ ์ง์ ‘ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” AOP๋ฅผ ํ™œ์šฉํ•ด์„œ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

public class BlockMemberService {
    private MemberRepository memberRepository;

    @PreAuthorize("hasRole('ADMIN')")
    public void block(String memberId) {
        Member member = memberRepository.findById(memberId);
        if (member == null) throw new NoMemberException();
        member.block();
    }
    // ...
}

๊ฐœ๋ณ„ ๋„๋ฉ”์ธ ๋‹จ์œ„๋กœ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋‹ค์†Œ ๊ตฌํ˜„์ด ๋ณต์žกํ•ด์ง„๋‹ค.

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

public class DeleteArticleService {
    public void delete(String userId, Long articleId) {
        Article article = articleRepository.findById(articleId);
        checkArticleExistence(article);
        permissionService.checkDeletePermission(userId, article);
        article.markDeleted();
        // ...
    }
}

permissionService.checkDeletePermission(์‚ฌ์šฉ์ž ID, ๊ฒŒ์‹œ๊ธ€) : ์‚ฌ์šฉ์ž๊ฐ€ ํ•ด๋‹น ๊ฒŒ์‹œ๋ฌผ ์‚ญ์ œ ๊ถŒํ•œ์„ ๊ฐ€์กŒ๋Š”์ง€ ๊ฒ€์‚ฌ

์กฐํšŒ ์ „์šฉ ๊ธฐ๋Šฅ๊ณผ ์‘์šฉ์„œ๋น„์Šค

์„œ๋น„์Šค์—์„œ์˜ ์กฐํšŒ ๊ธฐ๋Šฅ : ๋‹จ์ˆœ ์กฐํšŒ ์ „์šฉ ๊ธฐ๋Šฅ ํ˜ธ์ถœ

public class OrderListService {
    public List<OrderView> getOrderList(String ordererId) {
        return orderViewDao.selectByOrderer(ordererId);
    }
    // ...
}

์„œ๋น„์Šค์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์ด ์—†์„๋ฟ๋”๋Ÿฌ ์กฐํšŒ ์ „์šฉ ๊ธฐ๋Šฅ์ด์–ด์„œ ํŠธ๋žœ์žญ์…˜์ด ํ•„์š”ํ•˜์ง€๋„ ์•Š๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ๋ผ๋ฉด ๊ตณ์ด ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ํ•„์š” ์—†์ด ํ‘œํ˜„ ์˜์—ญ์—์„œ ๋ฐ”๋กœ ์กฐํšŒ ์ „์šฉ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค.

public class OrderController {
    
    private OrderViewDao orderViewDao;
    
    @RequestMapping("/myorders")
    public String list(ModelMap model) {
        String ordererId = SecurityContext.getAuthentication().getid();
        List<OrderView> orders = orderViewDao.selectByOrderer(ordererId);
        model.addAttribute("orders", orders);
        return "order/list";
    }
    // ...
}

์‘์šฉ ์„œ๋น„์Šค๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฐ•๋ฐ•๊ด€๋…์„ ๊ฐ€์ง€๋ฉด, ์ปจํŠธ๋กค๋Ÿฌ์™€ ๊ฐ™์€ ํ‘œํ˜„ ์˜์—ญ์—์„œ ์‘์šฉ ์„œ๋น„์Šค ์—†์ด ์กฐํšŒ ์ „์šฉ ๊ธฐ๋Šฅ์ด๋‚˜ ๋„๋ฉ”์ธ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์ฒ˜์Œ์—๋Š” ์ด์ƒํ•˜๊ฒŒ ๋Š๊ปด์งˆ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์‘์šฉ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉ์ž ์š”์ฒญ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ๋ณ„๋‹ค๋ฅธ ๊ธฐ์—ฌ๋ฅผ ํ•˜์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๊ตณ์ด ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•„๋„ ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

image

โš ๏ธ **GitHub.com Fallback** โš ๏ธ