'이미지 업로드'에 대한 오류 원인파악과 해결이 끝이 났다.
로그인을 하고 프로필페이지로 가보면 이렇게 '회원정보'가 비어있는것을 알 수 있다.
내용을 적으러 가보면 '에러'가 발생한다
-> '세션'이 없다는 에러문구이다. 우리가 저번시간에 배웠던 Open In View 에러와 같은 문제이다. UserController에 '회원수정페이지'를 응답하는 메서드를 살펴보면
여기도 sysout이 있어서 그런듯 하다. sysout을 '주석'처리하고 다시 '회원 수정 페이지'로 돌아가보자
-> 데이터를 입력하고 '제출'버튼을 클릭하면 아무런 이벤트도 발생하지 않는다. VSCode 콘솔을 확인해보면 StackOverFlow 오류가 발생한것을 볼 수 있다.
DB에서 User 테이블을 확인해보면 내가 입력한 데이터는 INSERT된 것을 볼 수 있다. 어떤 이유 때문에 '무한 참조'가 발생했을까?
UserApiController로 가서 '회원정보 수정' 데이터를 응답하는 메서드로 가보자.
@RequiredArgsConstructor
@RestController //데이터를 응답할것이기 때문
public class UserApiController {
private final UserService userService; //DI 주입
@PutMapping("/api/user/{id}")
public CMRespDTO<?> update(@PathVariable int id,
@Valid UserUpdateDto userUpdateDto,
BindingResult bindingResult, //꼭 Vaild가 적혀있는 다음 파라미터에 적어야함 !!!!
@AuthenticationPrincipal PrincipalDetails principalDetails) { //세션정보 변경
if (bindingResult.hasErrors()) { //오류가 발생하면 getFieldErrors() 컬렉션에 모아준다
Map<String, String> errorMap = new HashMap<>();
for (FieldError error : bindingResult.getFieldErrors()) {
errorMap.put(error.getField(), error.getDefaultMessage()); //20이하여야 합니다
}
throw new CustomValidationApiException("유효성 검사 실패", errorMap); //예외 발생시킴
} else {
User userEntity = userService.회원수정(id, userUpdateDto.toEntity()); //userObject를 날린다
principalDetails.setUser(userEntity); //세션정보 변경
return new CMRespDTO<>(1, "회원수정 완료", userEntity); //응답의 DTO. 1은 성공
//응답시에 userEntity의 모든 getter함수가 호출되고 JSON으로 파싱하여 응답한다.
}
}
}
즉, 우리가 저번 시간에 배웠던 Entity 응답시 자바 객체를 JSON으로 파싱하면서 발생하는 오류인것이다.
이 역시 LAZY 로딩으로 image 오브젝트를 호출하면서 발생하는 문제이다.
Image 오브젝트에도 User 오브젝트가 들어있기 때문에 발생하는 '양방향 매핑'의 문제이다.
이 에러를 해결하기 위해선 User 오브젝트를 호출할 때는 Image 오브젝트도 호출되는데
User를 호출하여 같이 호출된 Image 오브젝트 내부에 있는 User 오브젝트는 호출되지 않도록 해주면 된다.
User 클래스로 돌아가보자.
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity // DB에 테이블을 생성
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //번호 증가 전략이 데이터베이스를 따라간다.
private int id; //Primary 키
@Column(length = 20, unique = true) //회원가입 아이디 중복방지
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String name;
private String website; //웹 사이트
private String bio; //자기 소개
@Column(nullable = false)
private String email;
private String phone;
private String gender;
private String profileImageUrl; //사진
private String role; //권한
// 나는 연관관계의 주인이 아니다. 그러므로 테이블에 컬럼을 만들지마
// User를 Select할때 해당 User id로 등록된 image들을 전부 가져와
// Lazy = User를 Select할때 해당 User id로 등록된 image들을 가져오지마 - 대신 getImages() 함수의 image들이 호출될때 가져와
// Eager = User를 Select할때 해당 User id로 등록된 image들을 전부 Join해서 가져와
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@JsonIgnoreProperties({"user"})
private List<Image> images; // 양방향 매핑 - 프로필페이지를 응답할때, 같이 담아올 image의 정보
private LocalDateTime createDate;
@PrePersist // DB에 INSERT 되기 직전에 실행
public void createDate() {
this.createDate = LocalDateTime.now();
}
}
-> Image 오브젝트에 @JsonIgnoreProperties 어노테이션을 걸어준다.
@JsonIgnoreProperties는 JSON으로 파싱하지 않게 해주는 어노테이션이다.
괄호, 중괄호 안에 해당 오브젝트가 가지고 있는 변수를 지정해줄 수 있는데, 문제가 된 변수는 User 오브젝트이고, Image 오브젝트 내부의 user변수명을 적어주면 되겠다.
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Image { //N:1
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String caption; //해당 Image를 설명하는 영역
private String postImageUrl; //Image을 전송받아서 그 사진을 서버의 특정 폴더에 저장 - DB에 그 저장된 경로를 INSERT
@JoinColumn(name = "userId") // 1:1
@ManyToOne(fetch = FetchType.EAGER)
private User user; //1명의 User는 여러개의 Image를 만들어낼수 있다. - Image를 누가 올렸는지 알기위해 받은 USerObject
//이미지 좋아요 기능 (추후에 업데이트)
//이미지 좋아요 카운팅 (추후에 업데이트)
//댓글 기능 (추후에 업데이트)
private LocalDateTime createDate; //모든 DB에는 시간정보가 필요하다 - 데이터가 입력된 시간
@PrePersist
public void createDate() {
this.createDate = LocalDateTime.now();
}
/**오브젝트를 콘솔에 출력할 때 문제가 될 수 있어서 User부분을 출력되지 않게 함
* @Override
public String toString() {
return "Image{" +
"id=" + id +
", caption='" + caption + '\'' +
", postImageUrl='" + postImageUrl + '\'' +
", createDate=" + createDate +
'}';
}
**/
}
-> 다시 '회원 수정'을 시도해보면 성공적으로 수정이 완료되었다.
즉, User 호출 -> Image 호출 -> User 파싱안함 의 경로가 되는 것이다.
그렇다면 반대로 Image 호출 -> User 호출 -> Image 파싱안함 도 해주어야한다. 이부분은 나중에 진행하도록 하겠다.
'메타코딩 SNS프로젝트' 카테고리의 다른 글
나는 단세포라 자아성찰과 동기부여를 매일 해야한다 (0) | 2022.07.01 |
---|---|
34. 프로필 페이지 - Image Count View 렌더링하기 (0) | 2022.06.27 |
32. 프로필 페이지 - image업로드 에러 해결 및 Open In View 개념 잡기 (0) | 2022.06.27 |
31. 프로필 페이지 - Image 프로필페이지에 View 렌더링하기 (0) | 2022.06.24 |
30. 프로필 페이지 - Image 양방향 매핑 이해하기 (0) | 2022.06.21 |