엔티티를 API에 그대로 노출하는것은 좋지않다.
기존 List에서 List로 변경
브라우저와 서버 통신 : fetch
서버와 서버 통신 : RestTemplate
api통신할때는 csrf토큰을 끄는게 낫다.
csrf는 스프링시큐리티에서 get말고는 다 요청한다.
User user = new User("user1", "", List.of());
Authentication auth = new UsernamePasswordAuthenticationToken(
user,
user.getPassword(),
user.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(auth);
스프링 시큐리티를 통해서 로그인하면 세션에 Authentication 객체가 저장된다. SecurityContextHolder.getContext().getAuthentication()메서드로 현재 로그인된 사용자를 가져올수 있다.
영속성 컨
//fetch = lazy라는건 Member가 프록시라는 의미
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private Member author;
post를 json으로 보려고하니 fetch = lazy인 객체는 null이 아니라 가짜? 객체가 들어가 있다고한다.id만 있는.
FetchType.Lazy 인 녀석을 findBy 로 불러오면 해당 객체는 프록시 객체, 잭슨이 해당 프록시를 JSON 화 하려다가 실패했다. 그래서 일단 @JsonIgnore를 붙여놈
OneToMany는 기본적으로 페치가 lazy다
이런애들은 다 프록시 객체이고 id외에 특별한 정보 요청이 올때 쿼리가 발생한다.
단 new로 객체 생성되는 순간은 예외다.
DTO적용으로 해당 문제 해결
package com.ll.medium.domain.member.member.controller;
import com.ll.medium.domain.member.member.dto.MemberDto;
import com.ll.medium.domain.member.member.entity.Member;
import com.ll.medium.domain.member.member.service.MemberService;
import com.ll.medium.global.rsData.RsData.RsData;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
public class ApiV1MembersController {
private final MemberService memberService;
@Getter
@Setter
public static class LoginRequestBody {
private String username;
private String password;
}
@Getter
public static class LoginResponseBody {
private final MemberDto result;
public LoginResponseBody(Member member) {
result = new MemberDto(member);
}
}
@PostMapping("/login")
public RsData<LoginResponseBody> login(@RequestBody LoginRequestBody requestBody) {
RsData<Member> rsData = memberService.checkUsernameAndPassword(requestBody.getUsername(), requestBody.getPassword());
// return new RsData<LoginResponseBody>
return rsData.of(new LoginResponseBody(rsData.getData()));
}
}
2024-01-10T08:47:13.282+09:00 ERROR 41536 --- [omcat-handler-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: getOutputStream() has already been called for this response] with root cause
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:549) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:188) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
실수로 LoginResponseBody클래스에 @getter를 안붙였는데 해당 오류가 발생했다. 구글링해도 안나오고..
RsData 객체는 자동으로 JSON으로 변환되어 HTTP 응답 본문에 포함되는데. 이때 LoginResponseBody 클래스에 정의된 게터 메서드를 통해 필드의 값이 추출되어 JSON으로 변환되는 과정을 거친다. 이때 내부 메서드에서 게터가 없어서 그랬던거같다.. 커스텀 리스폰스바디 만들때는 꼭 게터를 붙이자.