3 Layered Architecture 개념을 적용하여 코드를 리팩토링함Controller , Service , Repository 레이어로 분리하여 책임을 나눔public interface MemoService {
}
// 실제 비즈니스 로직 관련 처리는 서비스 레이어의 구현체에서 진행
@Service
public class MemoServiceImpl implements MemoService {
}
@Service=@Component즉, Spring Bean으로 등록됨
명시적으로 Service 레이어를 나타내는 역할
public interface MemoRepository {
}
// 실제 DB와 상호작용하여 데이터를 CRUD하는 작업 수행
@Repository
public class MemoRepositoryImpl implements MemoRepository {
private final Map<Long, Memo> memoList = new HashMap<>();
}
@Repository=@Component즉, Spring Bean으로 등록됨
명시적으로 Repository 레이어를 나타는 역할
@RestController
@RequestMapping("/memos")
public class MemoController {
// 주입된 의존성을 변경할 수 없어 객체의 상태를 안전하게 유지할 수 있다.
private final MemoService memoService;
/**
* 생성자 주입
* 클래스가 필요로 하는 의존성을 생성자를 통해 전달하는 방식
* @param memoService @Service로 등록된 MemoService 구현체인 Impl
*/
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@PostMapping // 요청
public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto requestDto) {
// ServiceLayer 호출 및 응답
return new ResponseEntity<>(memoService.saveMemo(requestDto), HttpStatus.CREATED);
}
}
@RestController=@Controller+@ResponseBody
@RequestMapping: url의 prefix 설정
@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;
}
// 기존 사용하던 dto 대신 기본 자료형으로 매개변수 사용
// 메소드의 유연성과 재사용성이 높아짐
// 테스트 코드에도 도움 됨
public void update(String title, String contents) {
this.title = title;
this.contents = contents;
}
public void updateTitle(String title) {
this.title = title;
}
}
클래스 레벨에서
@Setter사용 시 전체 필드에Setter적용
필드 레벨에서 사용해야 원하는 필드에@Setter적용할 수 있음
public interface MemoService {
MemoResponseDto saveMemo(MemoRequestDto requestDto);
}
@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 없음 - Repository 영역이기 때문
Memo memo = new Memo(requestDto.getTitle(), requestDto.getContents());
// Inmemory DB에 Memo 저장
Memo savedMemo = memoRepository.saveMemo(memo);
return new MemoResponseDto(savedMemo);
}
}
public interface MemoRepository {
Memo saveMemo(Memo memo);
}
@Repository
public class MemoRepositoryImpl implements MemoRepository {
// 기존 MemoController에 있던 자료구조를 여기서 사용
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;
}
}
@RestController
@RequestMapping("/memos") // Prefix
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@GetMapping
public List<MemoResponseDto> findAllMemos() {
return memoService.findAllMemos();
}
}
public interface MemoService {
List<MemoResponseDto> findAllMemos();
}
@Service
public class MemoServiceImpl implements MemoService {
private final MemoRepository memoRepository;
public MemoServiceImpl(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
@Override
public List<MemoResponseDto> findAllMemos() {
return memoRepository.findAllMemos();
}
}
public interface MemoRepository {
List<MemoResponseDto> findAllMemos();
}
@Repository
public class MemoRepositoryImpl implements MemoRepository {
private final Map<Long, Memo> memoList = new HashMap<>();
@Override
public List<MemoResponseDto> findAllMemos() {
List<MemoResponseDto> allMemos = new ArrayList<>();
// DB에 있는 데이터를 하나씩 꺼내와서 allMemos 리스트에 저장
// HashMap<Memo> -> List<MemoResponseDto>
for (Memo memo : memoList.values()) {
MemoResponseDto responseDto = new MemoResponseDto(memo);
allMemos.add(responseDto);
}
return allMemos;
}
}
@RestController
@RequestMapping("/memos")
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@GetMapping("/{id}")
public ResponseEntity<MemoResponseDto> findMemoById(@PathVariable Long id) {
return new ResponseEntity<>(memoService.findMemoById(id), HttpStatus.OK);
}
}
public interface MemoService {
MemoResponseDto findMemoById(Long id);
}
@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 = memoRepository.findMemoById(id);
// ResponseEntity<HttpStatus.NOT_FOUND> 사용 불가
if (memo == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
"dose not exist id: " + id);
}
return new MemoResponseDto(memo);
}
}
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);
}
}
@RestController
@RequestMapping("/memos")
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@PutMapping("/{id}")
public ResponseEntity<MemoResponseDto> updateMemo(
@PathVariable Long id,
@RequestBody MemoRequestDto requestDto
) {
return new ResponseEntity<>(memoService.updateMemo(id, requestDto.getTitle(), requestDto.getContents()), HttpStatus.OK);
}
}
public interface MemoService {
MemoResponseDto updateMemo(Long id, String title, String contents);
}
@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);
}
}
메모리에 존재하는 Memo를 직접 수정함
DB 접근이 필요없음
@RestController
@RequestMapping("/memos")
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@PatchMapping("/{id}")
public ResponseEntity<MemoResponseDto> updateTitle(
@PathVariable Long id,
@RequestBody MemoRequestDto requestDto
) {
return new ResponseEntity<>(memoService.updateTitle(id, requestDto.getTitle(), requestDto.getContents()),
HttpStatus.OK);
}
}
public interface MemoService {
MemoResponseDto updateTitle(Long id, String title, String contents);
}
@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 is a required value.");
}
memo.updateTitle(title);
return new MemoResponseDto(memo);
}
}
메모리에 존재하는 Memo를 직접 수정함
DB 접근이 필요없음
@RestController
@RequestMapping("/memos")
public class MemoController {
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteMemo(@PathVariable Long id) {
memoService.deleteMemo(id);
// 성공한 경우
return new ResponseEntity<>(HttpStatus.OK);
}
}
public interface MemoService {
void deleteMemo(Long id);
}
@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);
}
}
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);
}
}
try-catch 해야 함RequestDto , ResponseDto 를 공유하여 null 이 들어오기도 한다.스프링 입문 - 6주차