나는 현재 DB에 있는 user , image 를 동시에 띄울 수 있는 프로필화면을 구현하고자 한다.
localhost:1234/user/1 하면 1번유저의 화면, localhost:1234/user/2 하면 2번 유저의 화면을 보고자한다.
위의 사진은 DB에서 가져온 것이 아닌 default 값으로 설정 해놓은것이다.
@RequiredArgsConstructor
@Controller
public class UserController{
private final UserService userService;
@GetMapping("user/{id}")
public String profile(@PathVariable int id, Model model){
User userEntity = userService.회원프로필(id);
model.addAttribute("user", userEntity);
return "user/profile";
}
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public User 회원프로필(int userId){
User userEntity = userRepository.findById(userId).orElseThrow(() ->{
throw new CustomException("해당 유저 프로필 페이지는 찾을 수 없습니다.");
});
return userEntity;
}
}
여기까진 User를 가지고 왔다, 근데 Image를 어떻게 받아올까? User를 받지않고 Image만 받아왔다면
User 정보는 어디서 가져오며 만약 Image와 User를 동시에 받아오면 id랑은 또 어떻게 연결해야하고 매우 번거롭다.
그렇기 때문에 우리는 양뱡향 매핑 을 사용하면 해결된다.
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(unique = true, length = 20)
private String username;
@Column(nullable = false)
private String password;
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;
private LocalDateTime createDate;
@JsonIgnore
@OneToMany(mappedBy ="user", fetch= FetchType.LAZY) //user는 Image의 변수를 넣어야함 //나는 연관관계의 주인이 아니다 를 알리는것.
//이걸 함으로써 DB가 컬럼을 만들지않으며 select 할때 user Id로 등록된 image들을 다 가져옴
// Lazy = User를 select할떄 해당 User id로 등록된 image들을 가져오지마 - 대신 getImages() 메소드가 호출될 때 가져와
// Eager = User를 select할때 해당 user Id로 등록된 image들을 전부 join해서 가져와!
private List<Image> images; // 양방향 매핑 / DB는 여러개못들어감 (리스트가 뭔지모름)
@PrePersist // DB에 INSERT 되기 직전에 실행
public void createDate(){
this.createDate = LocalDateTime.now();
}
}
내가 원하는 프로필을 보자면,
User 의 DB , Subscribe의 DB, Image의 DB 3가지 모두 필요하고, Image의 경우는 User에서 바로 가져올 방법이 없기 때문이다. 그렇기때문에
private List<Image> images; 를 추가하고 이를 OneToMany 로 연관관계 설정 후 mappedBy ="user" 를 통해 연관관계의 주인이 image가 아님을 알려줘서 DB가 List images 컬럼을 생성하지 못하게 한다. (DB는 원자성의 원리에 따라 하나의 컬럼에 여러개를 못넣기 떄문에 List가 뭔지모른다.) 또한
fetch를 Lazy타입으로 설정함으로서 getImage가 된 메소드만 호출하도록 설정. ★★ 아주 중요
LAZY
getImages() 메소드의 image가 호출되면 그때 셀렉트 해서 가져온다
EAGER
@JsonIgnore - JSON의 무한참조를 막는다 양방향 맵핑시에는 꼭 적어주자. 자세한 이유는 찾아보자
@RequiredArgsConstructor
@Controller
public class UserController {
private final UserService userService;
@GetMapping("/user/{id}")
public String profile(@PathVariable int id, Model model){
User userEntity = userService.회원프로필(id);
model.addAttribute("user",userEntity);
return "user/profile";
}
Service에서 받아온 값을 user 세션에 담아서 JSP로 보낸다.
<!--아이템들-->
<c:forEach var="image" items="${user.images}"> <!--EL표현식에서 변수명을 적으면 get함수가 자동호출 -->
<div class="img-box">
<a href=""> <img src="${image.postImageUrl}"/>
</a>
<div class="comment">
<a href="#" class=""> <i class="fas fa-heart"></i><span>0</span>
</a>
</div>
</div>
</c:forEach>
<!--아이템들end-->
이런식으로 평소처럼 EL표현식 사용하여 데이터를 가져오면, img src에 문제가 있어 엑박이뜨는걸 확인할 수 있다.
이는 DB에 저장된 경로와 실제 경로가 다르기 때문인데(실제 경로는 yml에 내장된 경로 + DB경로 이므로)
그렇기 떄문에 이를 설정해줘야 하는데,
Config 패키지에 WebMvcConfig 클래스를 만든다.
*Import 조심! 같은이름 가진거 있음
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer { //web 설정파일
@Value("${file.path}")
private String uploadFolder;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry
.addResourceHandler("/upload/**")
//jsp 페이지에서 /upload/** 이런 주소 패턴이 나오면 발동
.addResourceLocations("file:///" + uploadFolder)
// file:///C:/imagesaver/springbootwork/upload/ 이렇게 적힌다는소리
.setCachePeriod(60*10*6) // 1시간동안 캐싱
.resourceChain(true)
.addResolver(new PathResourceResolver()); // 붙여줘야 발동
}
}
해당 설정을 해준 뒤
<!--아이템들-->
<c:forEach var="image" items="${user.images}"> <!--EL표현식에서 변수명을 적으면 get함수가 자동호출 -->
<div class="img-box">
<a href=""> <img src="/upload/${image.postImageUrl}"/>
</a>
<div class="comment">
<a href="#" class=""> <i class="fas fa-heart"></i><span>0</span>
</a>
</div>
</div>
</c:forEach>
<!--아이템들end-->
img src의 경로를 수정하면 완성.
Q. LAZY 전략은 get 호출해야한다면서요
A. ${} 인 EL표현식은 get함수가 자동으로 호출되기 때문 ( 그냥 문법임, 편하게 쓰려고 만든 문법 get 자동으로 호출해줌)
<!--유저정보 및 사진등록 구독하기-->
<div class="profile-right">
<div class="name-group">
<h2>${user.username}</h2>
<button class="cta" onclick="location.href='/image/upload'">사진등록</button>
<button class="cta" onclick="toggleSubscribe(this)">구독하기</button>
<button class="modi" onclick="popup('.modal-info')">
<i class="fas fa-cog"></i>
</button>
</div>
<div class="subscribe">
<ul>
<li><a href=""> 게시물<span>3</span>
</a></li>
<li><a href="javascript:subscribeInfoModalOpen();"> 구독정보<span>2</span>
</a></li>
</ul>
</div>
<div class="state">
<h4>${user.bio}</h4>
<h4>${user.website}</h4>
</div>
</div>
그 외에 EL태그를 사용하여 가져올 데이터들을 적어주면 끝.
잘들어온다.
*출처 : TherePrograming(현 메타코딩)님의 인스타그램 클론코딩 유료강의
자세한건 https://cafe.naver.com/metacoding 참고 정말 좋은 강의입니다.
'Spring > JPA + Security' 카테고리의 다른 글
[Spring Data JPA]게시물 띄우는 로직 만들기 -2 뷰 렌더링 (0) | 2023.01.10 |
---|---|
[Spring Data JPA] 게시판 띄우는 로직 만들기 1-1 무한 참조 오류 (0) | 2023.01.10 |
[Spring Data JPA] JPA를 사용한 이미지 업로드 (0) | 2023.01.08 |
[Spring Data JPA] JPA를 사용한 구독하기 3 - 구독하기 로직 구현하기 (0) | 2023.01.08 |
[Spring Data JPA] JPA를 사용한 구독하기 2 - 네이티브 쿼리 짜기 (0) | 2023.01.08 |
댓글