2025.02.06(목)
필수
Lv 0. API 명세 및 ERD 작성
이 부분은 과제 구현 후 작성하고자 한다.
Lv 1. 일정 CRUD
application.properties 설정 주의하기
2025-02-06T16:28:25.400+09:00 INFO 5078 --- [Schedule-Develop] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info: Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)'] Database driver: undefined/unknown Database version: 9.2 Autocommit mode: undefined/unknown Isolation level: undefined/unknown Minimum pool size: undefined/unknown Maximum pool size: undefined/unknown
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: com.example.scheduledevelop.domain.entity.Schedule.username] with root cause
위와 같은 예외가 발생했다.
package 안에 있는 코드는 강의 실습 코드를 바탕으로 작성했으므로 큰 실수는 없을 것이라고 생각했다.
따라서 아마도 JPA 설정은 처음이라 설정 부분에서 빠진 부분이 있을 것이라고 판단했다.
application.properties에 아래와 같은 코드를 추가했더니 잘 작동했다.
spring.application.name=Schedule-Develop
spring.datasource.url=jdbc:mysql://localhost:3306/schedule-develop
spring.datasource.username=계정
spring.datasource.password=비밀번호
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
일정 생성 후, DB에서 생성일, 수정일 null 발생
위와 같이 생성일과 수정일에 null이 되었다.
@EnableJpaAuditing
@SpringBootApplication
public class ScheduleDevelopApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduleDevelopApplication.class, args);
}
}
원인은 ScheduleDevelopApplication 파일에
@EnableJpaAuditing을 하지 않은 것이 문제였다.
위와 같이 설정하고 다시 실행하면 아래와 같이 DB에 잘 들어온 것을 확인할 수 있다.
위와 같은 일을 겪고 나니 JPA 설정에 대해서는 적어도 잊어버리지 않은 것 같다는 생각이 들었다.
일정 단건 조회 구현 시, 컴파일 에러 발생
@GetMapping("/{scheduleId}")
public RequestEntity<ScheduleResponseDto> findById(@PathVariable("scheduleId") Long id){
ScheduleResponseDto scheduleResponseDto = scheduleService.findById(id);
return new RequestEntity<>(scheduleResponseDto,HttpStatus.OK);
}
위와 같이 responseEntity가 아닌 requestEntity를 사용해서 발생한 오류였다.
Response를 반환해야 한다고 머리로는 생각했으면서도
손은 그렇게 작성하지 못했고
눈은 그것을 판별하지 못했지만
다행히? 컴파일 에러로 보여서 잘 수정할 수 있었다.
Lv 2. 유저 CRUD
오늘 유저 자체의 CRUD는 구현을 완료했으나,
일정은 이제 작성 유저명 필드 대신 유저 고유 식별자 필드를 가집니다.
위의 부분에 대해서
일정 테이블과 유저 테이블의 연관 관계 매핑에 대해서
좀 더 고민해보고 구현할 예정이다.
2025.02.07(금)
필수
Lv 2. 유저 CRUD
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "com.example.scheduledevelop.domain.entity.Member.getName()" because the return value of "com.example.scheduledevelop.domain.entity.Schedule.getMember()" is null] with root cause
생성자 주입이나 Setter 주입을 통해 Member 값을 넣어줘야 하는데 그렇게 하지 않아서 발생했다.
public Schedule(String title, String contents, Member member) {
this.title = title;
this.contents = contents;
this.member = member;
}
따라서 위와 같이 Schedule 클래스의 생성자에 member도 포함시키고
Member findMember = memberRepository.findMemberByEmailOrElseThrow(memberEmail);
Schedule schedule = new Schedule(title, contents, member);
Service에서 email로 해당 Member를 찾아온 후,
생성자에 member 값을 세팅해주었다.
필수
Lv 4. 로그인(인증)
일정 생성, 수정, 삭제시 로그인 Session 값을 가져오는 방법
로그인 기능 자체는 구현을 완료했다.
하지만 로그인 후, 일정을 생성, 수정, 삭제할 때 해당 세션 값을 가져오는 방법에 대해 고민이 생겼다.
그래서 숙련 Spring 2주차 강의에서 Session 부분을 다시 들으며 복습했다.
그리고 강의에서 해답을 찾을 수 있었다.
Spring에서는 Session을 쉽게 다루도록 @SessionAttribute라는 어노테이션이 제공된다고 한다.
사실 다른 방법도 있지만 더 깔끔해 보여서 나는 이 어노테이션을 사용하기로 햇다.
@SessionAttribute
- request.getSession(true); 와는 다르게 Session을 새로 생성하는 기능은 없다.
- 이미 로그인이 완료된 사용자를 찾는 경우 즉, Session이 있는 경우에 사용한다.
이미 AuthController에서 Session을 생성하기 때문에 생성 기능은 필요하지 않았다.
일정 Controller에서는 로그인 후, 즉 Session이 있는 경우에 사용하면 되는 것이었다.
@PostMapping
public ResponseEntity<ScheduleResponseDto> save(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
@RequestBody ScheduleSaveRequestDto requestDto) {
// 생성 로직
ScheduleResponseDto scheduleResponseDto
= scheduleService.save(requestDto.getTitle(), requestDto.getContents(),loginMember);
return new ResponseEntity<>(scheduleResponseDto, HttpStatus.CREATED);
}
따라서 Controller에서 해당 Session을 매개변수로 받아서 Member타입의 변수에 저장했다.
그리고 Service에 같이 매개변수로 넣었다.
@Transactional
public ScheduleResponseDto save(String title, String contents, Member member) {
Schedule schedule = new Schedule(title, contents, member);
Schedule savedSchedule = scheduleRepository.save(schedule);
return new ScheduleResponseDto(
savedSchedule.getId(),
savedSchedule.getTitle(),
savedSchedule.getContents(),
savedSchedule.getMember().getName(),
savedSchedule.getMember().getEmail()
);
}
이를 Service에서 Schedule 생성자를 통해 loginMember 값을 주입할 수 있었다.
작성자만 일정을 수정, 삭제하게 하는 방법
위에서 매개변수로 받아온 loginMember 변수를 활용하기로 했다.
각각의 id가 동일한지 비교하는 로직을 만들고
같지 않다면 예외처리를 하고,
같다면 수정, 삭제 로직을 수행하도록 할 계획이었다.
그러나 위와 같은 생각으로 구현을 하고 Postman을 통해 테스트를 하는데
이상한 점을 발견하게 되었다.
분명 작성자인데 수정, 삭제가 작동하지 않는 것이었다.
System.out.println("findMemberId = " + findMemberId);
위와 같이 print를 통해 각각의 id를 조회하고 나니 원인을 찾을 수 있었다...
memberId와 scheduleId를 비교하고 있으니 둘이 다를 수 밖에...
따라서 아래와 같이 findMember라는 새로운 Service 메서드를 만들어서
scheduleId에 해당하는 Member를 찾고 그 Member의 아이디를 찾아서 이를 비교했다.
@PatchMapping("/{scheduleId}")
public ResponseEntity<Void> updateTitleAndContents(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
@PathVariable("scheduleId") Long id,
@RequestBody UpdateTitleAndContentsRequestDto requestDto
) {
Member findMember = scheduleService.findMember(id);
Long findMemberId = findMember.getId();
// 작성자만 수정 가능
if (!loginMember.getId().equals(findMemberId)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// 수정 로직
scheduleService.updateTitleContents(id, requestDto.getTitle(), requestDto.getContents());
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/{scheduleId}")
public ResponseEntity<Void> delete(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
@PathVariable("scheduleId") Long id
){
Member findMember = scheduleService.findMember(id);
Long findMemberId = findMember.getId();
// 작성자만 삭제 가능
if (!loginMember.getId().equals(findMemberId)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// 삭제 로직
scheduleService.delete(id);
return new ResponseEntity<>(HttpStatus.OK);
}
오늘은 계획대로 필수 과제 구현을 모두 끝내게 되어서 뿌듯하다.
다음 주에는 도전과제를 도전할 계획이다.
다음 주도 화이팅이다!
'CODING > 스파르타 내일배움캠프 TIL' 카테고리의 다른 글
38_3진법 뒤집기_일정관리 Develop 개인과제 Lv 2,3,4 필수과제 구현완료_25.2.7(금) (0) | 2025.02.07 |
---|---|
37_최대공약수와 최소공배수_일정관리 개인과제 피드백_일정관리 Develop 개인과제 Lv 1, 2 구현중_25.2.6(목) (0) | 2025.02.06 |
36_직사각형 별찍기_스탠다드 트랜잭션, 인덱스_숙련 Spring 3주차_25.2.5(수) (0) | 2025.02.05 |
35_행렬의 덧셈_일정관리 개인과제 해설_숙련 Spring 1,2주차_25.2.4(화) (1) | 2025.02.04 |
34_문자열 다루기 기본_스탠다드 데이터베이스 연관관계 매핑_일정관리 과제 Lv 5 구현완료_25.2.3(월) (0) | 2025.02.03 |