👇🏻 이전 글
2024.08.05 - [Spring] - Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기
Spring 프로젝트를 3 Layer Architecture로 역할 분리하기
3 Layer Architecture 란 처리 과정을 크게 Controller, Service, Repository 로 나누는 것을 뜻함.
- Controller : 클라이언트에서 온 요청을 받아 Service로 넘겨주고 처리 완료된 결과를 클라이언트에게 보여주는 역할
- Service : 사용자의 요구 사항을 처리. 비즈니스 로직 구현하는 곳. DB 저장 및 조회가 필요한 경우 Repository에 요청.
- Repository : DB 관리. CRUD 작업 처리.
Controller에서 Service 분리하기
Controller는 클라이언트에게서 받은 요청을 그에 알맞는 Service로 넘겨주는 역할이므로 Controller에서 구현된 코드 대부분을 Serviec로 옮기게 된다.
MemoService.java
public class MemoService {
private final JdbcTemplate jdbcTemplate;
public MemoService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB 저장
KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체
String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
jdbcTemplate.update( con -> {
PreparedStatement preparedStatement = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, memo.getUsername());
preparedStatement.setString(2, memo.getContents());
return preparedStatement;
},
keyHolder);
// DB Insert 후 받아온 기본키 확인
Long id = keyHolder.getKey().longValue();
memo.setId(id);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
}
- 먼저 Service 패키지를 만들고 안에 MemoService 클래스를 만든다.
- MemoService에서도 JDBCtemplate를 사용해야 하기 때문에 Controller와 동일하게 JDBCtemplate를 받는 생성자를 만들어준다.
- Service안에 구현될 메소드는 Controller와 동일한 이름과 파라미터를 받는 createMemo로 만든다.
- Controller의 createMemo의 반환 타입이 MemoResponseDto 였기 때문에 Service의 메소드 반환 타입 또한 동일하게 해준다.
MemoController.java
public class MemoController {
private final JdbcTemplate jdbcTemplate;
public MemoController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@PostMapping("/memos")
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
MemoService memoService = new MemoService(jdbcTemplate);
return memoService.createMemo(requestDto);
}
}
위와 같은 방법으로 Controller에서 Service를 분리해주면 Controller에는 간결한 코드만이 남게 된다.
Service에서 Repository 분리하기
DB 연결 및 CRUD 작업은 Repository에서 이루어져야 하므로 Service에서 Repository 부분을 분리한다.
MemoRepository.java
public class MemoRepository {
private final JdbcTemplate jdbcTemplate;
public MemoRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Memo save(Memo memo) {
KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체
String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
jdbcTemplate.update( con -> {
PreparedStatement preparedStatement = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, memo.getUsername());
preparedStatement.setString(2, memo.getContents());
return preparedStatement;
},
keyHolder);
// DB Insert 후 받아온 기본키 확인
Long id = keyHolder.getKey().longValue();
memo.setId(id);
return memo;
}
}
- Repository 패키지를 만든 후 안에 MemoRepository 클래스를 만든다.
- DB와 연결되는 곳이기 때문에 JDBCtemplate를 사용할 수 있도록 생성자를 구현한다.
- Memo 객체를 파라미터로 받는 save라는 이름의 메소드를 선언한다.
- MemoService에서는 Controller에서 받아온 requestDto를 자바 객체로 변환해주는 작업을 마친 뒤 MemoRepository로 넘겨주게 된다.
- MemoRepository는 넘겨받은 객체로 나머지 DB 작업을 하게 된다.
MemoService.java
public class MemoService {
private final JdbcTemplate jdbcTemplate;
public MemoService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB 저장
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
}
MemoService는 반환 값으로 MemoResponseDto를 뱉어야 하기 때문에 Repository를 통해 DB에 저장하고 나면 사용한 memo 객체를 responseDto로 변환해서 반환한다.
조회하기, 수정하기, 삭제하기 기능 또한 위와 같은 방법으로 3계층에 나눠 분리 할 수 있다.
다만 수정하기와 삭제하기에서 사용하는 메소드 findById( )는 DB 조회 메소드이기 때문에 Repository에 적어주게 되는데 이를 Service에서 사용하기 위해 접근제어자를 private → public으로 변경해줘야 한다.
수정하기 기능 예시 >
public Long updateMemo(Long id, MemoRequestDto requestDto) {
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
// 해당 메모가 DB에 존재하는지 확인
Memo memo = memoRepository.findById(id);
if(memo != null) {
// memo 내용 수정
memoRepository.update(id, requestDto);
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
객체 중복 생성 문제 해결
Controller와 Service 코드를 보면 JDBCtemplate를 넘겨주는 코드 한 줄이 메소드 마다 반복되어 적혀 있는 것을 확인 할 수 있다. 이는 생성자 주입으로 다음과 같이 해결 할 수 있다.
MemoController.java
private final MemoService memoService;
public MemoController(JdbcTemplate jdbcTemplate) {
this.memoService = new MemoService(jdbcTemplate);
}
위와 같이 적으면 MemoController를 생성할 때 생성자를 통해 자동으로 MemoService에 JDBCtemplate을 넘겨주게 되고 MemoService 선언도 한번만 하면 된다.
같은 방법으로 Service에서 Repository로 JDBCtemplate 넘겨주는 부분도 변경해준다.
TIL 💭
이렇게 역할이나 용도에 따라 코드를 나누는 것은 프로젝트를 한 눈에 파악하기도 쉽고 코드를 작성하거나 수정 할 때 등 모든 면에서 편리한 것 같다.
'Spring' 카테고리의 다른 글
Spring의 Filter 이해하기 및 구현하기 (0) | 2024.08.16 |
---|---|
Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지 (0) | 2024.08.16 |
Spring의 JPA와 Entity 이해하기 (0) | 2024.08.07 |
Spring의 IoC(제어의 역전), DI(의존성 주입) 이해하기 (0) | 2024.08.06 |
Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기 (0) | 2024.08.05 |