오늘은 Spring의 3 Layer Architecture에 대해 학습하였다. 기존 메모장 프로젝트의 구조적 문제점을 개선하기 위해 Controller, Service, Repository 간의 역할을 분리하고, 각 레이어의 역할을 명확히 구분하는 방법을 적용하였다.
기존 메모장 프로젝트는 모든 API를 MemoController 클래스에서 처리하고 있었다. 이는 현재는 단순해 보이지만, 기능이 추가되고 복잡해질수록 여러 문제가 발생할 수 있다:
이를 해결하기 위해 3 Layer Architecture를 도입하였다. 이 구조는 크게 Controller, Service, Repository 세 개의 레이어로 나뉘며, 각자의 역할을 명확히 분리함으로써 코드의 유지보수성과 확장성을 높인다.
이 아키텍처를 도입함으로써, 코드가 모듈화되고 유지보수가 더 쉬워진다.
MemoController는 클라이언트로부터 요청을 받아 그 데이터를 MemoService로 전달하고, 처리된 결과를 응답하는 역할만을 담당한다.
@RestController
@RequestMapping("/api")
public class MemoController {
private final MemoService memoService;
// MemoService를 의존성 주입 방식으로 사용
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@PostMapping("/memos")
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
return memoService.createMemo(requestDto); // Service에 메모 생성 로직을 위임
}
@GetMapping("/memos")
public List<MemoResponseDto> getMemos() {
return memoService.getMemos(); // Service에 메모 조회 로직을 위임
}
@PutMapping("/memos/{id}")
public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
return memoService.updateMemo(id, requestDto); // Service에 메모 수정 로직을 위임
}
@DeleteMapping("/memos/{id}")
public Long deleteMemo(@PathVariable Long id) {
return memoService.deleteMemo(id); // Service에 메모 삭제 로직을 위임
}
}
Controller는 비즈니스 로직을 처리하지 않고, 단순히 요청을 MemoService에 넘기는 역할만 수행한다. Service는 실제 비즈니스 로직을 처리하는 곳이다.
Service는 비즈니스 로직을 처리하며, 데이터베이스와 관련된 작업이 필요할 때는 MemoRepository에 그 작업을 요청한다.
@Service
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
Memo memo = new Memo(requestDto); // RequestDto를 Memo 엔티티로 변환
Memo savedMemo = memoRepository.save(memo); // DB 저장을 Repository에 위임
return new MemoResponseDto(savedMemo); // 저장된 결과를 ResponseDto로 변환
}
public List<MemoResponseDto> getMemos() {
return memoRepository.findAll(); // 모든 메모 조회를 Repository에 위임
}
public Long updateMemo(Long id, MemoRequestDto requestDto) {
Memo memo = memoRepository.findById(id); // 메모 조회
if (memo != null) {
memo.update(requestDto); // 내용 수정
memoRepository.update(id, requestDto); // 수정된 내용을 DB에 반영
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
public Long deleteMemo(Long id) {
Memo memo = memoRepository.findById(id); // 메모 조회
if (memo != null) {
memoRepository.delete(id); // 메모 삭제를 Repository에 위임
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
}
Service는 클라이언트의 요청을 처리하는 비즈니스 로직을 담당하며, 데이터베이스와 상호작용이 필요할 때 Repository를 사용한다. 비즈니스 로직이 깔끔하게 분리되어 가독성이 좋아진다.
MemoRepository는 데이터베이스와 직접 상호작용하는 부분으로, CRUD 작업을 처리한다. 모든 데이터베이스 관련 로직이 이 레이어에서 수행된다.
@Repository
public class MemoRepository {
private final JdbcTemplate jdbcTemplate;
public MemoRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Memo save(Memo memo) {
String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
jdbcTemplate.update(sql, memo.getUsername(), memo.getContents());
return memo;
}
public List<MemoResponseDto> findAll() {
String sql = "SELECT * FROM memo";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
Long id = rs.getLong("id");
String username = rs.getString("username");
String contents = rs.getString("contents");
return new MemoResponseDto(id, username, contents);
});
}
public void update(Long id, MemoRequestDto requestDto) {
String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
}
public void delete(Long id) {
String sql = "DELETE FROM memo WHERE id = ?";
jdbcTemplate.update(sql, id);
}
public Memo findById(Long id) {
String sql = "SELECT * FROM memo WHERE id = ?";
return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
Memo memo = new Memo();
memo.setUsername(rs.getString("username"));
memo.setContents(rs.getString("contents"));
return memo;
}, id);
}
}
Repository는 데이터베이스와의 CRUD 작업을 수행하며, 이 작업은 모두 Repository에서 처리된다. 따라서 데이터베이스 관련 로직이 한 곳에 집중되어 관리가 편리하다.
오늘 학습한 3 Layer Architecture를 통해 기존 메모장 프로젝트의 구조적 문제를 개선할 수 있었으며, 앞으로도 이 구조를 사용하여 유지보수성과 가독성을 높이는 프로젝트를 진행할 수 있을 것이다.