클라이언트에서 요청을 받아 서버가 결과값을 반환할때 처리과정이 비슷함에 따라 크게 Controller, Service, Repository 3개로 분리하는 것을 말한다.

Controller는 클라이언트의 요청을 받는역활을 합니다.
ex) Controller 예시
package com.sparta.memo.controller;
import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.service.MemoService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@PostMapping("/memos")
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
return memoService.createMemo(requestDto);
}
@GetMapping("/memos")
public List<MemoResponseDto> getMemos() {
return memoService.getMemos();
}
@GetMapping("/memos/contents")
public List<MemoResponseDto> getMemosByKeword(String keyword){
return memoService.getMemosByKeyword(keyword);
}
@PutMapping("/memos/{id}")
public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
return memoService.updateMemo(id,requestDto);
}
@DeleteMapping("/memos/{id}")
public Long deleteMemo(@PathVariable Long id) {
return memoService.deleteMemo(id);
}
}
@Controller 또는 @RestController를 사용하며 url주소을 맵핑해주는 역활을 합니다.
Service는 Controller에서 받은 요청을 수행합니다. Service안에 서버의 주요 로직들이 들어가며 기능이 많아 질수록 Service가 점점 커집니다.
ex) Service 예시
package com.sparta.memo.service;
import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
return new MemoResponseDto(memo);
}
public List<MemoResponseDto> getMemos() {
return memoRepository.findAllByOrderByModifiedAtDesc().stream().map(MemoResponseDto::new).toList();
}
public List<MemoResponseDto> getMemosByKeyword(String keyword) {
return memoRepository.findAllByContentsContainsOrderByModifiedAtDesc(keyword).stream().map(MemoResponseDto::new).toList();
}
@Transactional
public Long updateMemo(Long id, MemoRequestDto requestDto) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
memo.update(requestDto);
return id;
}
public Long deleteMemo(Long id) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
memoRepository.delete(memo);
return id;
}
private Memo findMemo(Long id) {
return memoRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
);
}
}
Repository는 Service를 구현하기 위한 DB값을 가져오거나 저장하는 역활을 하게 됩니다.

ex) Repository 예시
package com.sparta.memo.repository;
import com.sparta.memo.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface MemoRepository extends JpaRepository <Memo, Long>{
List<Memo> findAllByOrderByModifiedAtDesc();
List<Memo> findAllByContentsContainsOrderByModifiedAtDesc(String keyword);
}
데이터베이스의 데이터를 다루는 코드들이 입력됩니다.
이처럼 3 Layer Architecture를 사용하여 개발시 기능이 많아져도 코드를 이해하기 쉬어지며 유지보수에 도움이 됩니다.
