[백엔드 야생 개발일지] 게시글 등록 기능 만들어보기 (2)
저번 시간에 했던 것.
1. 카테고리 테이블, 서브카테고리 테이블, 게시글 테이블을 생성했다.
2. 게시글 생성일, 수정일 자동으로 db에 저장되게 함.
우선 한 가지 확인하고 싶은게 생겼다. 이전에 필터를 만들어둔 상태고, 로그인/회원가입을 제외한 곳들은 전부다 막아둔 상태다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.csrf().disable() // CSRF 공격에 대한 방어를 해제
.cors().and() // cors 허용
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 서버를 stateless하게 유지 즉 세션 X
.and()
.formLogin().disable() // 시큐리티가 기본 제공하는 로그인 화면 없앰. JWT는 로그인과정을 수동으로 클래스로 만들어야 하니까
.httpBasic().disable() // 토큰 방식을 이용할 것이므로 보안에 취약한 HttpBasic은 꺼두기
.authorizeRequests() // HttpServletRequest를 사용하는 요청들에 대한 접근제한을 설정하겠다
.requestMatchers("/account/signup", "/account/login", "/").permitAll() // 요 세 놈에 대한 요청은 인증없이 접근 허용
.anyRequest().authenticated() // 나머지에 대해선 인증을 받아야 한다.
.and()
// 여러 필터들 중 UsernamePassword필터 앞에 내가 만든 필터를 둔다. 이렇게 하면 커스텀 필터로 인가인증을 다룰 수 있음
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}
게시글에 관한 것은 BoardController에서 일괄적으로 처리하게 할 계획이다. 이 때 /board로 들어오는 놈들은 인증을 받아야 한다!
@RestController
@RequestMapping(value = "/board")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
@GetMapping("/test")
public String boardTest() {
return "test";
}
}
한마디로 /board/test로 생으로 그냥 들어가면 test라는 응답을 받을 수 없을 거고, 토큰을 함께 보내면 test라는 응답을 받을 수 있다는 소리가 되는데.. 정말 잘 동작하는가?를 시험해보고 싶었다.
우선 생으로 /board/test로 보내봤다.
결과는 실패..
이번엔 토큰을 헤더에 담고 보내봤다. 결과는..?
성공한 모습!! 😄😄
참고로 위는 유효기간이 지난 토큰인데, 이렇게 유효하지 않은 토큰을 담아 보내면 응답이 오지 않는, 즉 필터가 일을 잘 하고 있는 모습을 볼 수 있었다.
이제 글 작성 api를 만들 차례. 컨트롤러에 다음과 같은 createBoard메서드를 추가해줬다.
@PostMapping("/write")
public String createBoard(@RequestBody BoardDTO boardDTO) {
Board newBoard = new Board();
newBoard.setWriter(boardDTO.getEmail());
newBoard.setTitle(boardDTO.getTitle());
newBoard.setContent(boardDTO.getContent());
newBoard.setCategoryId(boardDTO.getCategoryId());
newBoard.setLike(0L);
newBoard.setHits(0L);
boardService.registerNewBoard(newBoard);
return "success";
}
BoardDTO는 다음과 같다.
@Getter
@Setter
public class BoardDTO {
private String email;
private String title;
private String content;
@JsonProperty("category_id")
private int categoryId;
}
RequestBody는 예전에 작성했듯 클라이언트가 보낸 json데이터를 자바객체로 매핑해주는 역할을 하는데, 이 때 json데이터의 키 값과 매핑될 클래스의 멤버변수들 이름이 같아야 정상적으로 착착 매핑된다. 그러나 json에서 category_id라는 snake형식으로 보낸 값이 클래스에서 camel형식으로 작성된 categoryId에 매핑시키고 싶다면, 저렇게 해당 필드에 @JsonProperty라는 어노테이션을 멕이면 된다.
게시글이 잘 등록되는 걸 볼 수 있다!
현재 이 게시판 등록 기능은, 클라이언트 측에서 json으로 작성자의 이메일을 함께 넘겨주는 방식이다. 그러나 구글에 게시글 등록과 관련된 포스트들을 보면, 이메일이 아니라 멤버자체를 게시글 엔티티의 필드로 설정하는 방식도 있었다. 차이점이 뭔지는 잘 모르겠으나..일단 한 번 따라해봤다.
우선 Board클래스를 다음과 같이 수정해줬다.
@Getter
@Setter
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_id")
private Long boardId;
@JoinColumn(name = "writer", nullable = false)
@ManyToOne(fetch = FetchType.LAZY)
private Member writer;
@Column(length = 80)
private String title;
// ... 생략
기존엔 write필드를 String으로 해서 이메일 자체를 세팅해줬었지만, 보시다시피 멤버로 받는 모습.
@ManyToOne(fetch = FetchType.LAZY) : 게시글 입장에선 멤버와 다대일(N : 1) 관계임. 이 다중성을 나타내는 어노테이션. FetchType은 즉시로딩과 지연로딩에 관한 거라는데..음...뭔 소린지 몰라서 패쓰
@JoinColumn(name = "writer") : 외래키를 매핑할 때 사용하는 어노테이션. name은 외래키의 이름, 즉 조인의 대상으로 사용할 컬럼의 이름을 지정해줌. Board테이블의 writer컬럼에 Member의 pk인 email이 들어갈 것임을 명시하는 것!! Member클래스에서 email필드에 @Id 어노테이션에 멕여져 있어서 가능.
그리고 BoardDTO에서 다음과 같이 email필드를 빼줬다.
@Getter
@Setter
public class BoardDTO {
private String title;
private String content;
@JsonProperty("category_id")
private int categoryId;
}
그리고 BoardController에서 다음과 같이 인증정보를 가져와서 멤버를 꽂아넣도록(?) 바꿔줬다.
@PostMapping("/write")
public String createBoard(@AuthenticationPrincipal Member member, @RequestBody BoardDTO boardDTO) {
Board newBoard = new Board();
newBoard.setWriter(member);
newBoard.setTitle(boardDTO.getTitle());
newBoard.setContent(boardDTO.getContent());
newBoard.setCategoryId(boardDTO.getCategoryId());
newBoard.setLike(0L);
newBoard.setHits(0L);
boardService.registerNewBoard(newBoard);
return "success";
}
@AuthenticationPrincipal : UserDetails를 구현한 인증객체를 주입할 때 사용하는 어노테이션. 요청을 보낸 사용자가 인증된 경우SecurityContextHolder라는 곳에 저장된 인증객체(현재사용자의 정보!)를 꺼내와 넣어주는 역할! 나같은 경우는 Member자체가 UserDetails를 구현하도록 만들었기 때문에 Member로 받도록 했다.
결과는..
서..성공!
첫 질문으로 들어와서, 그럼 게시글 등록을 할 때 Board엔티티의 writer필드를 String으로 하는게 맞는걸까 아님 Member로 하는게 맞는걸까? 이는 연관관계 매핑이라는 것과 관련이 있다고 한다. 이것에 대해서 알아봐야겠다.