End to End 프로젝트
3계층에 대한 이해
API 설계하기 (CRUD) 와 구현
왜 필요할가?
Memo라는 것을 구현하려면 필요한 Table을 만들어야 함
→ 이러한 기능을 Repository에서 domain이 담당하게됨
→ domain > Memo.java 클래스를 만들어 준다.
@NoArgsConstructor // 기본생성자를 만듭니다.
@Getter // getter를 lombok으로 쉽게 표현
@Entity // 테이블과 연계됨을 스프링에게 알려줍니다. DB설계도로 할거야를 알려줌
public class Memo extends Timestamped { // 생성,수정 시간을 자동으로 만들어줍니다.
// Timestamped를 상속받아 기능을 추가(생성시간, 수정시간 변수화)
@GeneratedValue(strategy = GenerationType.AUTO) // 1씩 증가하게 자동생성
@Id // Primary key를 지정
private Long id; // 인스턴스 변수 1
@Column(nullable = false)
private String username; // 인스턴스 변수 2
@Column(nullable = false)
private String contents; // 인스턴스 변수 3
public Memo(String username, String contents) { // 생성자를 만들어줌
this.username = username; // 기본 생성자가 필요하지만 어노테이션 처리함
this.contents = contents;
}
public Memo(MemoRequestDto requestDto) {
this.username = requestDto.getUsername();
this.contents = requestDto.getContents();
}
public void update(MemoRequestDto requestDto){ // 업데이트 메소드
this.username = requestDto.getUsername(); // return 없음
this.contents = requestDto.getContents(); // DTO를 통해서 update하게 됨
}
}
- DB를 직접 바꿀일은 없으므로 Setter는 필요 없음, Getter만 필요
- record를 생성할때 정해진 파라미터가 필요하므로 생성자가 필요함
- requestsDto를 통해서 데이터를 이동시키므로 생성자의 파라미터 타입 MemoRequestDto
왜 필요할가?
SQL과 같은 기능을 하는 것은 Repository 따라서 MemoRepository를 만들어 줌
이때 JpaRepository의 메서드만 사용할 것이므로 상속후 인터페이스로 만들어 줌
public interface MemoRepository extends JpaRepository<Memo, Long> { // 상속 후 JPA 메서드 사용
List<Memo> findAllByOrderByModifiedAtDesc(); // 수정시간 기준 오름차순으로 List안에 Memo 객체를 넣어줌
}
- JPA 메서드만을 사용하는 인터페이스를 만드는데 Memo DB이므로 MemoRepository를 만듬
왜 필요할가?
DB에 직접 접근하여 값을 수정하는 것은 데이터 분석에도 금기 되어 있는 사실!
따라서 값의 이동이 있을 때 DTO를 사용
@Getter
public class MemoRequestDto {
private String username; // 컬럼 중 하나
private String contents; // 컬럼 중 하나
}
- 자동생성되는 ID는 제외하고 memo table의 column을 멤버 변수로 갖는다.
- 또한 private 멤버 변수를 조회하기 위해 Getter 옵션을 사용한다.
왜 필요할가?
일단은 Client에서 DB로 넘어가서 update를 해준다는 점에서 Service에 해당이 됨
추후 DB 관련 update는 모두 Service에서 구현하는지 궁금함
또한, 서비스에 대한 이해가 진짜 필요함
@RequiredArgsConstructor // final은 꼭 필요한 녀석으로 생성자도 필요로 함
@Service // Service임을 알려주고 있음
public class MemoService {
private final MemoRepository memoRepository; //꼭 필요한 녀석
@Transactional // 트랜젝션 기능
public Long update(Long id, MemoRequestDto requestDto) {
Memo memo = memoRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
);
memo.update(requestDto);
return memo.getId();
}
}
- 일단 Client단에서 DB로 데이터의 이동이기 때문에 Service에 update를 만들어 놓는 것임
왜 필요할가?
API 요청을 처리하기 위해 필요하다.
즉, API 설계도에 따라 적절한 GET, PUT, DELETE, POST에 따라 반환되는 값이 무엇인지 설정해주게 됨
@RequiredArgsConstructor // final에 꼭 필요한 녀석들은 생성자를 필요로 함
@RestController // Json 형태로 객체 데이터를 반환하기 위함
public class MemoController {
private final MemoRepository memoRepository; // 꼭 필요한 녀석들을 멤버 변수로 설정
private final MemoService memoService;
@PostMapping("/api/memos") // Post요청
public Memo createMemo(@RequestBody MemoRequestDto requestDto) { // Json으로 입력받으면
Memo memo = new Memo(requestDto); // Memo 객체 생성 후
return memoRepository.save(memo); // DB에 저장
}
@GetMapping("/api/memos") // Get 요청
public List<Memo> getMemos() { // GET이므로 BODY에 json은 없음
return memoRepository.findAllByOrderByModifiedAtDesc(); // 조회해줌
}
@DeleteMapping("/api/memos/{id}")
public Long deleteMemo(@PathVariable Long id) { //URL path로 부터 파라미터를 받음
memoRepository.deleteById(id);
return id;
}
@PutMapping ("/api/memos/{id}")
public Long updateMemo(@PathVariable Long id,@RequestBody MemoRequestDto requestDto){
memoService.update(id,requestDto); //Service인 update를 이용
return id;
}
}
느낀점!
- 객체 지향 개념을 모두 이해하면서 Spring에 대입하는 것이 완벽하게 되지 않는다는 것
(기초가 부족)- 아직도 Service의 모호한 개념이 머리에 있어서 추가 공부가 필요함
- Annotation은 그때그때 쓰임새를 알아둘 것
- 기본적으로 CRUD를 구현할 자신감은 생김!!