๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Today I Learned(TIL)/์ŠคํŒŒ๋ฅดํƒ€ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„

์ฃผํŠน๊ธฐ ์ž…๋ฌธ/์ˆ™๋ จ_3 Layer Architecture

by carrot0911 2024. 12. 4.

Layered Architecture ์‹ค์Šต

0. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

๋”๋ณด๊ธฐ

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

  • ๋ฉ”๋ชจ์žฅ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ์™€ ๋™์ผํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • ๊ผญ ํ™•์ธํ•ด์•ผ ํ•  ์‚ฌํ•ญ
    • Name: layered
    • Language: Java
    • Build system: Gradle - Groovy
    • JDK: 17
    • ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ(Location)๋Š” ์ด์ „์— ๋งŒ๋“ค์—ˆ๋˜ workspace ํด๋”๋กœ ์ง€์ •

์ดˆ๊ธฐ ํด๋ž˜์Šค ์ƒ์„ฑ

Memo

@Getter
@AllArgsConstructor
public class Memo {

    private Long id;
    private String title;
    private String contents;

    public void update(MemoRequestDto dto) {
        this.title = dto.getTitle();
        this.contents = dto.getContents();
    }

    public void updateTitle(MemoRequestDto dto) {
        this.title = dto.getTitle();
    }

}

MemoRequestDto

@Getter
public class MemoRequestDto {

    private String title;
    private String contents;

}

MemoResponseDto

@Getter
public class MemoResponseDto {

    private Long id;
    private String title;
    private String contents;

    public MemoResponseDto(Memo memo) {
        this.id = memo.getId();
        this.title = memo.getTitle();
        this.contents = memo.getContents();
    }

}

MemoController

@RestController
@RequestMapping("/memos")
public class MemoController {

    private final Map<Long, Memo> memoList = new HashMap<>();

    @PostMapping
    public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto dto) {

        // ์‹๋ณ„์ž๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ ํ•˜๋„๋ก ๋งŒ๋“ฆ
        Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;

        // ์š”์ฒญ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ Memo ๊ฐ์ฒด ์ƒ์„ฑ
        Memo memo = new Memo(memoId, dto.getTitle(), dto.getContents());

        // Inmemory DB์— Memo ๋ฉ”๋ชจ
        memoList.put(memoId, memo);

        return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.CREATED);
    }
}

1. Controller ๋ถ„๋ฆฌํ•˜๊ธฐ

๋”๋ณด๊ธฐ

์ฑ…์ž„ ๋ถ„๋ฆฌ

  • Controller์˜ ์—ญํ• 
    • ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
    • ์š”์ฒญ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ Service Layer์— ์ „๋‹ฌํ•œ๋‹ค.
    • Service์—์„œ ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋œ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ตํ•œ๋‹ค.
/*
Controller์˜ ์—ญํ• 
- ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
- ์š”์ฒญ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ Service Layer์— ์ „๋‹ฌํ•œ๋‹ค.
- Service์—์„œ ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋œ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ตํ•œ๋‹ค.
 */

@RestController
@RequestMapping("/memos")
public class MemoController {

    // Controller์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค (Repository) - ์ž๋ฃŒ๊ตฌ์กฐ
    private final Map<Long, Memo> memoList = new HashMap<>();

    /*
    1. ์š”์ฒญ (Controller)
    ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ์ค‘์ด๋‹ค.
    @RequestBody๋ฅผ ์‚ฌ์šฉํ•ด์„œ HttpMessageConverter๊ฐ€ ๋™์ž‘ํ•ด์„œ JSON ๋ฐ์ดํ„ฐ๊ฐ€ dto ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜๋œ๋‹ค.
     */
    @PostMapping
    public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto dto) {

        /*
        2. ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง
        MemoId ์‹๋ณ„์ž ๊ณ„์‚ฐ (Repository)
        ์‹๋ณ„์ž๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ ํ•˜๋„๋ก ๋งŒ๋“ฆ
        ์ด๊ฒƒ์€ ์›๋ž˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์—ญํ• ์ด๋‹ค. -> Repository๋กœ ์˜ฎ๊ธธ ์˜ˆ์ •
         */
        Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;

        /*
        ์š”์ฒญ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ Memo ๊ฐ์ฒด ์ƒ์„ฑ (Service)
        ์ƒ์„ฑํ•œ Repository๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ €์žฅํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์€ Service Layer์˜ ์˜์—ญ์ด๋‹ค. -> Service Layer๋กœ ์˜ฎ๊ธธ ์˜ˆ์ •
         */
        Memo memo = new Memo(memoId, dto.getTitle(), dto.getContents());

        /*
        3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํ˜ธ์ž‘์šฉ
        Inmemory DB์— Memo ์ €์žฅ (Repository)
        ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์‹ค์ œ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ถ€๋ถ„ -> Repository๋กœ ์˜ฎ๊ธธ ์˜ˆ์ •
         */
        memoList.put(memoId, memo);

        // 4. ์‘๋‹ต (Controller)
        return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.CREATED);
    }
}
  • ์š”์ฒญ, ์‘๋‹ต์„ ์ œ์™ธํ•œ ๋ชจ๋‘๋ฅผ Controller์—์„œ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

Service Layer ๋ถ„๋ฆฌ

  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
public interface MemoService {
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
}

Repository Layer ๋ถ„๋ฆฌ

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํ˜ธ์ž‘์šฉ
public interface MemoRepository {
}
/**
 * Annotation @Repository๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Repository Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * DB์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ CRUDํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Repository
public class MemoRepositoryImpl implements MemoRepository {

		private final Map<Long, Memo> memoList = new HashMap<>();
 
}

2. ๋ฉ”๋ชจ ์ƒ์„ฑ API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

  • ์š”์ฒญ, Srvice Layer ํ˜ธ์ถœ, ์‘๋‹ต
@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
     public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ์ƒ์„ฑ API
     * @param : {@link MemoRequestDto} ๋ฉ”๋ชจ ์ƒ์„ฑ ์š”์ฒญ ๊ฐ์ฒด
     * @return : {@link ResponseEntity<MemoResponseDto>} JSON ์‘๋‹ต
     */
    @PostMapping // ์š”์ฒญ
    public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto requestDto) {
				// ServiceLayer ํ˜ธ์ถœ ๋ฐ ์‘๋‹ต
        return new ResponseEntity<>(memoService.saveMemo(requestDto), HttpStatus.CREATED);
    }
}

Memo

@Getter
@AllArgsConstructor
public class Memo {

    @Setter
    private Long id;
    private String title;
    private String contents;

    public Memo(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }

    public void update(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }

    public void updateTitle(String title) {
        this.title = title;
    }

}

Service

  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
public interface MemoService {
	MemoResponseDto saveMemo(MemoRequestDto requestDto);
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {

    private final MemoRepository memoRepository;

    public MemoServiceImpl(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }

    @Override
    public MemoResponseDto saveMemo(MemoRequestDto requestDto) {

        // ์š”์ฒญ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ Memo ๊ฐ์ฒด ์ƒ์„ฑ ID ์—†์Œ
        Memo memo = new Memo(requestDto.getTitle(), requestDto.getContents());

        // Inmemory DB์— Memo ์ €์žฅ
        Memo savedMemo = memoRepository.saveMemo(memo);

        return new MemoResponseDto(savedMemo);
    }
}

Repository

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํ˜ธ์ž‘์šฉ
public interface MemoRepository {
	
		Memo saveMemo(Memo memo);

}
@Repository
public class MemoRepositoryImpl implements MemoRepository {

    private final Map<Long, Memo> memoList = new HashMap<>();

    @Override
    public Memo saveMemo(Memo memo) {

        // memo ์‹๋ณ„์ž ์ž๋™ ์ƒ์„ฑ
        Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;
        memo.setId(memoId);

        memoList.put(memoId, memo);

        return memo;
    }
}

3. ๋ฉ”๋ชจ ๋ชฉ๋ก ์กฐํšŒ API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ์ „์ฒด ์กฐํšŒ API
     * @return : {@link List<MemoResponseDto>} JSON ์‘๋‹ต
     */
    @GetMapping
    public List<MemoResponseDto> findAllMemos() {

        return memoService.findAllMemos();
    }
    
}

Service

public interface MemoService {

	List<MemoResponseDto> findAllMemos();
	
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public List<MemoResponseDto> findAllMemos() {

        // ์ „์ฒด ์กฐํšŒ
        List<MemoResponseDto> allMemos = memoRepository.findAllMemos();

        return allMemos;
    }

}

Repository

public interface MemoRepository {
		
    List<MemoResponseDto> findAllMemos();

}
@Repository
public class MemoRepositoryImpl implements MemoRepository {

    private final Map<Long, Memo> memoList = new HashMap<>();

    @Override
    public Map<Long, Memo> findAllMemos() {

				// init List
        List<MemoResponseDto> allMemos = new ArrayList<>();

        // HashMap<Memo> -> List<MemoResponseDto>
        for (Memo memo : memoList.values()) {
            MemoResponseDto responseDto = new MemoResponseDto(memo);
            allMemos.add(responseDto);
        }

        return allMemos;
    }
    
}

4. ๋ฉ”๋ชจ ๋‹จ๊ฑด ์กฐํšŒ API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ๋‹จ๊ฑด ์กฐํšŒ API
     * @param id ์‹๋ณ„์ž
     * @return : {@link ResponseEntity<MemoResponseDto>} JSON ์‘๋‹ต
     */
    @GetMapping("/{id}")
    public ResponseEntity<MemoResponseDto> findMemoById(@PathVariable Long id) {

        return new ResponseEntity<>(memoService.findMemoById(id), HttpStatus.OK);
    }
    
}

Service

public interface MemoService {

	MemoResponseDto findMemoById(Long id);
	
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public MemoResponseDto findMemoById(Long id) {
        // ์‹๋ณ„์ž์˜ Memo๊ฐ€ ์—†๋‹ค๋ฉด?
        Memo memo = memoRepository.findMemoById(id);

        // ์•„๋ž˜ ์ฝ”๋“œ ์‚ฌ์šฉ ๋ถˆ๊ฐ€.
        // if (memo == null) {
        //     return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        // }

        return new MemoResponseDto(memo);
    }

}

Repository

public interface MemoRepository {
		
    Memo findMemoById(Long id);

}
@Repository
public class MemoRepositoryImpl implements MemoRepository {

    private final Map<Long, Memo> memoList = new HashMap<>();

    @Override
    public Memo findMemoById(Long id) {

        return memoList.get(id);
    }
    
}

์˜ˆ์™ธ์ฒ˜๋ฆฌ

  • Layer ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์€ DTO๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค.
    • ์ ์ ˆํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public MemoResponseDto findMemoById(Long id) {
        // ์‹๋ณ„์ž์˜ Memo๊ฐ€ ์—†๋‹ค๋ฉด?
        Memo memo = memoRepository.findMemoById(id);

        // NPE ๋ฐฉ์ง€
        if (memo == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Does not exist id = " + id);
        }

        return new MemoResponseDto(memo);
    }

}

5. ๋ฉ”๋ชจ ์ „์ฒด ์ˆ˜์ • API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ์ „์ฒด ์ˆ˜์ • API
     * @param id ์‹๋ณ„์ž
     * @param : {@link MemoRequestDto} ๋ฉ”๋ชจ ์ˆ˜์ • ์š”์ฒญ ๊ฐ์ฒด
     * @return : {@link ResponseEntity<MemoResponseDto>} JSON ์‘๋‹ต
     * @exception ResponseStatusException ์š”์ฒญ ํ•„์ˆ˜๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ 400 Bad Request, ์‹๋ณ„์ž๋กœ ์กฐํšŒ๋œ Memo๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ 404 Not Found
     */
    @PutMapping("/{id}")
    public ResponseEntity<MemoResponseDto> updateMemo(
            @PathVariable Long id,
            @RequestBody MemoRequestDto requestDto
    ) {

        return new ResponseEntity<>(memoService.updateMemo(id, requestDto.getTitle(), requestDto.getContents()), HttpStatus.OK);
    }
    
}

Service

public interface MemoService {

	MemoResponseDto updateMemo(Long id, String title, String contents);
	
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public MemoResponseDto updateMemo(Long id, String title, String contents) {
        // memo ์กฐํšŒ
        Memo memo = memoRepository.findMemoById(id);

        // NPE ๋ฐฉ์ง€
        if (memo == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Does not exist id = " + id);
        }

        // ํ•„์ˆ˜๊ฐ’ ๊ฒ€์ฆ
        if (title == null || contents == null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The title and content are required values.");
        }

        // memo ์ˆ˜์ •
        memo.update(title, contents);

        return new MemoResponseDto(memo);
    }

}
@Getter
@AllArgsConstructor
public class Memo {

    private Long id;
    private String title;
    private String contents;

    public void update(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }

    public void updateTitle(MemoRequestDto dto) {
        this.title = dto.getTitle();
    }

}

Repository

  • ๋ฉ”๋ชจ๋ฆฌ ์ƒ(memoList)์— ์กด์žฌํ•˜๋Š” Memo๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ์ด ํ•„์š” ์—†๋‹ค.

6. ๋ฉ”๋ชจ ์ œ๋ชฉ ์ˆ˜์ • API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ์ œ๋ชฉ ์ˆ˜์ • API
     * @param id ์‹๋ณ„์ž
     * @param : {@link MemoRequestDto} ๋ฉ”๋ชจ ์ˆ˜์ • ์š”์ฒญ ๊ฐ์ฒด
     * @return : {@link ResponseEntity<MemoResponseDto>} JSON ์‘๋‹ต
     * @exception ResponseStatusException ์š”์ฒญ ํ•„์ˆ˜๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ 400 Bad Request, ์‹๋ณ„์ž๋กœ ์กฐํšŒ๋œ Memo๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ 404 Not Found
     */
    @PatchMapping("/{id}")
    public ResponseEntity<MemoResponseDto> updateTitle(
            @PathVariable Long id,
            @RequestBody MemoRequestDto requestDto
    ) {

        return new ResponseEntity<>(memoService.updateTitle(id, requestDto.getTitle(), requestDto.getContents()), HttpStatus.OK);
    }
    
}

Service

public interface MemoService {

	MemoResponseDto updateTitle(Long id, String title, String contents);
	
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public MemoResponseDto updateTitle(Long id, String title, String contents) {
        // memo ์กฐํšŒ
        Memo memo = memoRepository.findMemoById(id);

        // NPE ๋ฐฉ์ง€
        if (memo == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Does not exist id = " + id);
        }
        // ํ•„์ˆ˜๊ฐ’ ๊ฒ€์ฆ
        if (title == null || contents != null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The title and content are required values.");
        }

        memo.updateTitle(title);

        return new MemoResponseDto(memo);
    }

}

Repository

  • ๋ฉ”๋ชจ๋ฆฌ ์ƒ(memoList)์— ์กด์žฌํ•˜๋Š” Memo๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ์ด ํ•„์š” ์—†๋‹ค.

7. ๋ฉ”๋ชจ ์‚ญ์ œ API ๋ฆฌํŒฉํ† ๋ง

๋”๋ณด๊ธฐ

Controller

@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {

    // ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    private final MemoService memoService;

    /**
     * ์ƒ์„ฑ์ž ์ฃผ์ž…
     * ํด๋ž˜์Šค๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
     * @param memoService @Service๋กœ ๋“ฑ๋ก๋œ MemoService ๊ตฌํ˜„์ฒด์ธ Impl
     */
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    /**
     * ๋ฉ”๋ชจ ์‚ญ์ œ API
     * @param id ์‹๋ณ„์ž
     * @return {@link ResponseEntity<Void>} ์„ฑ๊ณต์‹œ Data ์—†์ด 200OK ์ƒํƒœ์ฝ”๋“œ๋งŒ ์‘๋‹ต.
     * @exception ResponseStatusException ์‹๋ณ„์ž๋กœ ์กฐํšŒ๋œ Memo๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ 404 Not Found
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteMemo(@PathVariable Long id) {

        memoService.deleteMemo(id);
        // ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ
        return new ResponseEntity<>(HttpStatus.OK);
    }
    
}

Service

public interface MemoService {

	void deleteMemo(Long id);
	
}
/**
 * Annotation @Service๋Š” @Component์™€ ๊ฐ™๋‹ค, Spring Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋Š” ๋œป.
 * Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 * ๋ช…์‹œ์ ์œผ๋กœ Service Layer ๋ผ๋Š”๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
 * ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 */
@Service
public class MemoServiceImpl implements MemoService {
	
		private final MemoRepository memoRepository;
	
	  public MemoServiceImpl(MemoRepository memoRepository) {
	      this.memoRepository = memoRepository;
	  }
		
    @Override
    public void deleteMemo(Long id) {
        // memo ์กฐํšŒ
        Memo memo = memoRepository.findMemoById(id);

        // NPE ๋ฐฉ์ง€
        if (memo == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Does not exist id = " + id);
        }

        memoRepository.deleteMemo(id);

    }

}

Repository

public interface MemoRepository {
		
    void deleteMemo(Long id);

}
@Repository
public class MemoRepositoryImpl implements MemoRepository {

    private final Map<Long, Memo> memoList = new HashMap<>();

    @Override
    public void deleteMemo(Long id) {

        memoList.remove(id);
    }
    
}

ํ•ด๊ฒฐํ•œ ๋ฌธ์ œ์ 

  • Controller์˜ ์ฑ…์ž„์„ Layer ๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜์˜€๋‹ค.

๋ฌธ์ œ์ 

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค. (Database ์ ‘๊ทผ ๊ธฐ์ˆ )
  • ์—์™ธ ๋ฐœ์ƒ ์‹œ ๊ณตํ†ต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ๊ฐ๊ฐ์˜ ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ try-catchํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • RequestDto, ResponseDto๋ฅผ ๊ณต์œ ํ•˜์—ฌ null๊ฐ’์ด ๋“ค์–ด์˜ค๊ธฐ๋„ ํ•œ๋‹ค.
    • ํ•„์š” ์—†๋Š” ํ•„๋“œ์— ์ถ”๊ฐ€์ ์ธ null ๊ฒ€์‚ฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
  • Spring Bean, ์ƒ์„ฑ์ž ์ฃผ์ž… ๋“ฑ Spring์˜ ๋™์ž‘ ์›๋ฆฌ์— ๋Œ€ํ•ด ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.
  • ์™œ Interface๋กœ ๋งŒ๋“ค์–ด์„œ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค.