-
프로필 페이지 4Spring Boot/프로필 페이지 만들기 2021. 12. 16. 17:49
사진 업로드 사이트에서 업로드 하면, 프로필 화면에 그 사진이 나타나도록 하겠다.
<프로필 화면> - profile.jsp 파일
UserController.java 파일에서
profil 메서드가
return "user/profile"; 할 때
Model 객체에 이미지들을 담아서, 같이 리턴해주면 된다.
요청 주소를 /user/{id} 로 해서
/user/1 이런식으로 오면, 1번 유저의 프로필 화면이(profile.jsp)
/user/2 이런 식으로 오면, 2번 유저의 프로필 화면(profile.jsp ) 이 나오도록 하겠다.
<UserController.java> 파일
@GetMapping("/user/{id}") public String profile(@PathVariable int id, Model model) { User userEntity = userService.회원프로필(id); model.addAttribute("user", userEntity); return "user/profile"; }
--------------------------
UserController.java 파일에서 UserService.java 의 회원프로필 메서드를 요청해서 프로필 정보를 가져온다.
<UserService.java > 파일
@Transactional public User 회원프로필(int userId) { User userEntity = userRepository.findById(userId).orElseThrow(()-> { throw new CustomException("해당 프로필 페이지는 없는 페이지입니다."); }); return userEntity; }
만약 없는 사용자의 프로필 페이지를 요청하면, 에러메세지를 보여주도록 처리
3번 사용자가 데이터베이스에 없는데,
3번 사용자의 프로필 화면을 요청 ( /user/3 이런식으로 주소 요청)하면 아래와 같은 에러메세지 나옴
하지만, /user/1 이런 식으로
데이터베이스에 있는 1번 유저 번호로 주소를 보내면
프로필 화면으로 잘 이동한다.
이러한 에러 처리 위해 CustomException.java 파일 만듦
public class CustomException extends RuntimeException{ private static final long serialVersionUID = 1L; public CustomException(String message) { super(message); } }
ControllerExceptionHandler.java 파일에 CustomException을 낚아채도록 아래와 같이 만듦
@ExceptionHandler(CustomException.class) public String exception(CustomException e) { return Script.back(e.getMessage()); }
즉, CustomException 이 요청되면, ControllerExceptionHandler.java 에 처리해준대로, 아래 코드가 실행되서 리턴됨
return Script.back(e.getMessage());
----------------------------------------------------------------------------------------------------------------
그런데, 프로필 화면으로 이동 시, 업로드한 이미지(image 정보) 뿐만 아니라
게시물 수, 프로필 사진 등 user 관련 여러 데이터, 구독 정보(subscribe 정보)를 같이 들고 가야 하므로,
user 데이터를 데이터베이스로부터 select 해서 영속성 컨텍스트에 넣을 때,
image 정보도 같이 가져올 수 있도록 양방향 매핑 사용
기존에, Image.java 파일에, 업로드한 유저 정보도 같이 넣기 위해,
@Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class Image { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String caption; private String postImageUrl; @JoinColumn(name = "userId") @ManyToOne(fetch = FetchType.EAGER) private User user; private LocalDateTime createDate; @PrePersist public void createDate() { this.createDate = LocalDateTime.now(); } }
이 처럼 user 변수도 같이 넣었다.
User.java 파일에서는 원래 image 변수를 사용하지 않았는데, 양방향 매핑을 위해 image 변수 추가
<User.java> 파일
@Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(length = 100, 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 role; private String profileImageUrl; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List<Image> images; private LocalDateTime createDate; @PrePersist public void createDate() { this.createDate = LocalDateTime.now(); } }
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
<1> @OneToMany : 한 명의 유저는 여러 개의 이미지 만듦
mappedBy = "user" : Image.java 파일에서 User 객체 변수 이름, 즉 user 로 아래와 같이 사용했는데, 이 값을 넣어줘야 함
private User user;
<2> mappedBy 뜻 :
1) 나는 연관관계의 주인이 아니다. 그러니 테이블(데이터베이스)에 칼럼 만들지 마라.
(특히, images 변수가 List 타입이다. 그런데 데이터베이스에는 collection 타입이 없어서 이를 만들 수 없다.)
2) User 를 select 할 때 해당 User id 로 등록된 image 들을 다 가져와(그런데 이 때, 위에서 fetch 타입에 따라 다양한 결과)
즉, 연관관계의 주인은, image 테이블의 user 변수임.
private User user;
<3> fetch = FetchType.LAZY
타입 LAZY : User 를 select 할 때 해당 User id 로 등록된 image들을 가져오지마라. 단, getImages() 함수 image들이 호출될 때 가져오기
타입 EAGER : User 를 select 할 때 해당 User id 로 등록된 image 들을 전부 join 해서 가져오기
LAZY 와 EAGER 전략에 대해 살펴보겠다.
1) LAZY 전략
UserService.java 에서 아래와 같이 user를 select 할 떄, images를 가져오지 않음
User userEntity = userRepository.findById(userId).orElseThrow(()-> { throw new CustomException("해당 프로필 페이지는 없는 페이지입니다.");
즉, 이 UserService.java 코드는, 사용자가 /user/{id}로 요청할 때 실행되는 코드이므로,
사이트에서 /user/1 이렇게 요청을 해보고 sts(이클립스) 콘솔창에서 보면
데이터베이스 쿼리를 볼 수 있는데,
이런 식으로 쿼리가 나타나면서 맨 마지막에 보면
이런식으로, user 테이블에서만 select 했음을 알 수 있다.
2) EAGER 전략
그런데 만약 아래와 같이 EAGER 전략으로 바꾼뒤
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER) private List<Image> images;
사이트에서 /user/1 이렇게 요청을 하면
이런식으로, image와 user가 join 되서 같이 가져왔음을 알 수 있다.
3) LAZY 전략
그러면 이제 다시, LAZY 전략으로 바꾼 다음 언제 images 정보를 가져오는지 살펴보겠다.
@Transactional public User 회원프로필(int userId) { User userEntity = userRepository.findById(userId).orElseThrow(()-> { throw new CustomException("해당 프로필 페이지는 없는 페이지입니다."); }); System.out.println("================"); userEntity.getImages().get(0); return userEntity; }
============== 를 기준으로
위에에서는 user 데이터를 데이터베이스로부터 불러오고
아래에서는 user 데이터에 있는 image 데이터를 불러온다.
그러면 sts(이클립스) 콘솔창에서 보면
이런식으로 =======를 기준으로
위에서는 user 데이터만 가져오고(LAZY 전략을 사용했기 때문에, getImages() 를 사용하지 않는 한, user 데이터를 select 하면 user 데이터만 가져옴)
아래에서는 images 데이터를 가져온 것을 알 수 있다. (getImages() 를 사용했기 때문에, user 테이블과 양방향 매핑하고 있는 images 데이터도 가져옴)
더보기위에 나타난 쿼리의 끝까지 따라가보면, ========= 윗 부분에서는 아래와 같이 user 테이블로부터 데이터를 가져오고
=========== 아래에서는 아래와 같이 image 테이블로부터 데이터를 가지고 왔음을 알 수 있다.
여기서 get(0) 을 한 이유는,
첫번째 데이터까지 가져오라는 뜻.
getImages() 만 하는 것은, 객체를 호출하는 거임.
이처럼 양방향 매핑은,
controller 의 Model 객체로 jsp 파일에 데이터 전달 시
user 정보만 전달하더라도 image 정보까지 같이 전달할 수 있게 됨
참고 자료 : 이지업 강의 사이트 "스프링부트 SNS프로젝트 - 포토그램 만들기"
'Spring Boot > 프로필 페이지 만들기' 카테고리의 다른 글
프로필 페이지 7 - @JsonIgnoreProperties (0) 2021.12.18 프로필 페이지 5 (0) 2021.12.16 프로필 페이지 3 (0) 2021.12.15 프로필 페이지 2 (0) 2021.12.15 프로필 페이지 1 (0) 2021.12.14