오늘은 강의를 마저 들은 다음 과제에 돌입하기 시작한 날이다. 연관관계에 대한 매핑에 대해 들었고 기준을 정하는 것을 확실히 이해한 날이었다.
또한 현업이나 프로젝트를 만들 때 단방향을 선호하는 이유 또한 알게 되었다.
방향에 대한 기준
우선적으로 여러 Entity들의 관계들에 대해 생각을 해보자면, 여러 Entity들을 생성할 시 대부분에는 생성되는 개수의 차이가 분명히 존재한다.
지금하고 있는 프로젝트로 예를 들자면, 만들어야되는 Entity는 user와 schedule, comment 총 3가지로 볼 수 있다.
여기서부터 중요한 것은 이들의 생성원리에 대해서 생각을 해볼 필요가 있다.
누가먼저 생성되는지를 생각해보면 쉽게 답을 찾을 수 있다.
user가 있기에 schedule을 생성할 수 있다, schedule이 있기에 comment를 달 수가 있다.
따라서 comment -> schedule(N:1)관계, schedule -> user(N:1) 관계라고 볼 수 있다.
여러개의 댓글이 일정에 달리고 여러 일정들이 유저들에 의해 생성된다. 따라서 위 와 같은 관계와 방향이 이뤄질 수 있는 것이다.
강의를 통해 관계와 방향성을 찾는 법을 익힐 수 있었고, 프로젝트에 들어감에 앞서 관계매핑에 대한 이해가 되었기에 쉽게 틀을 짤 수 있게 되었다.
Comment Entity에는 Schedule에 관련된 필드를 갖고 이에 ManyToOne 어노테이션이 붙을 것이며, Schedule Entity에는 User에 관련된 필드를 갖으며 ManyToOne 어노테이션이 붙을 것이다.
이를 참고해서 ERD를 짤 수 있을 것이고, 몇 단계를 더 짚고 넘어갈 수 있다.
CRUD를 작성하며 파악하기
우선 전에 했던 프로젝트와 비슷한 구조가 상당히 많아 이번에는 강의를 참고하지 않고서, 타이핑하는 과정을 밟아보려고 하였다.
결과적으로는 뜨문뜨문 기억이 났기에 중간중간 코드에 구멍이 생기는 일이 발생하였다.
따라서 이 과정을 지속적으로 반복 타이핑하며, 어노테이션이나 구문들, 구상이 자연스럽게 떠오르도록 차근차근 다시 타이핑을 해보려고 하였다.
패키지 파일 미리 생성하기
우선 프로젝트를 생성하고 그에 맞는 세팅을 실시한다.
생성간에는 롬복,JPA,MySQL,Spring framework web 이 4가지를 설정에 챙겨가는 것을 잊지말자.
이후 resources -> application.properties에 마저 세팅을 해줘야한다.
spring.datasource.url=jdbc:mysql://localhost:****/test
url → MySQL 서버 주소
localhost:****/test → 내 PC의 **** 포트에 있는 test라는 DB에 연결
spring.datasource.username=root
username → DB 접속 계정 (root)
spring.datasource.password=****
password → 계정 비밀번호 (****)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
driver-class-name → MySQL용 JDBC 드라이버 클래스
spring.jpa.hibernate.ddl-auto=create
JPA가 **엔티티(Entity)**를 기준으로 DB 테이블을 어떻게 생성할지 정하는 옵션
create → 실행할 때마다 기존 테이블을 삭제 후 새로 생성
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLBDialect
Hibernate가 SQL을 생성할 때 사용하는 **DB 방언(Dialect)**
MySQLBDialect는 MySQL 전용 SQL 문법을 쓰겠다는 뜻
spring.jpa.show-sql=true
show-sql=true → 콘솔에 실행되는 SQL 쿼리를 출력
spring.jpa.properties.hibernate.format_sql=true
format_sql=true → SQL 문을 보기 좋게 줄바꿈/들여쓰기해서 출력
작성 유저명, 할일 제목, 할일 내용, 작성일, 수정일 필드작성일, 수정일 필드는 JPA Auditing을 활용합니다.
필요 어노테이션
이후 명세에 맞게 필드를 작성하고, id를 제외한 생성자를 추가한다.
extends 를 통해 JpaRespository를 추가한다.필요 어노테이션
하위 계층인 서비스를 필드로 갖는다.(의존성 주입)
필요 어노테이션
하위 계층인 레퍼지토리를 피드로 갖는다.(의존성 주입)
@PostMapping("/names")
public ResponseEntity<생성 응답 DTO> create (@RequestBody 생성 요청 DTO dto) {
return ResponseEntity.status(HttpStatus.OK).body(서비스.save(dto);
}
@Transactional
public 생성 응답 DTO save(생성 요청 DTO dto) {
Entity entity = new Entity(dto.get생성요청dto 필드값들());
Entity saveSchedule = 레퍼지토리.save(entity);
return new 생성응답 DTO(
saveSchedule.생성응답dto 필드값들()
);
}
@GetMapping("/names")
public ResponseEntity<List<전체 조회 응답 DTO>> getSchedules() {
return ResponseEntity.status(HttpStatus.OK).body(서비스.getAll());
}
@Transactional(readOnly = true)//조회
public List<전체 조회 응답 DTO> getAll() {
List<Entity> schedules = 레퍼지토리.findAll();
List<전체 조회 응답 DTO> dtos = new ArrayList<>();
for (Entity entity : schedules) {
전체 조회 응답 DTO dto = new 전체 조회 응답 DTO(
schedule.전체 조회 응답 DTO 필드값들()
);
dtos.add(dto);
}
return dtos;
}
@GetMapping("/schedules/{scheduleId}")
public ResponseEntity<선택 조회 응답 DTO> getSchedule(@PathVariable Long scheduleId) {
return ResponseEntity.status(HttpStatus.OK).body(서비스.getOne(scheduleId));
}
@Transactional(readOnly = true)//조회
public 선택 조회 응답DTO getOne(Long scheduleId) {
Entity entity = 레퍼지토리.findById(scheduleId).orElseThrow(
() -> new IllegalArgumentException("없는 일정입니다.")
);
return new 선택 조회 응답 DTO(
schedule.get선택 조회 응답 DTO 필드값들()
);
}
@PutMapping("/schedule/{scheduleId}")
public ResponseEntity<수정 응답 DTO> update(@PathVariable Long scheduleId, @RequestBody 수정 요청 DTO dto){
return ResponseEntity.status(HttpStatus.OK).body(서비스.update(scheduleId, dto));
}
@Transactional
public 수정 응답 DTO update(Long scheduleId, 수정 요청 DTO dto) {
Entity entity = 레퍼지토리.findById(scheduleId).orElseThrow(
() -> new IllegalArgumentException("없는 일정입니다.")
);
entity.update(dto.update 생성자에 맞게);
return new 수정 응답 DTO(
schedule.수정 응답 DTO 필드값들()
);
}
public void update(String title, String content) {
this.title = title;
this.content = content;
}
@DeleteMapping("/schedule/{scheduleId}")
public ResponseEntity<Void> delete(@PathVariable Long scheduleId){
서비스.delete(scheduleId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@Transactional
public void delete(Long scheduleId) {
boolean existence = 레퍼지토리.existsById(scheduleId);
if (!existence) {
throw new IllegalArgumentException("없는 일정입니다.");
}
레퍼지토리.deleteById(scheduleId);
}
각 CRUD별 코드 작성함에 있어 순서들을 정리하였고 구멍없이 매꾸기 위해 반복숙달할 예정이다. 각 기능별로 특징들이 잡혀있는 것들을 볼 수 있고 위 내용들이 헷갈리기 때문에 체득을 해야만 한다고 생각한다. 앞으로 프로젝트 뿐만아니라 가장 기본적인 요소이기에 중요하니 손가락에 익혀야만한다고 생각한다. 잊을만 하면 다시 찾아보기 위해 정리해본다.
반성하고 갑니다.