Jam's story
게시판 댓글 저장 , 삭제 기능 본문
ArticleCommentController
DTO -> 계층 간 데이터 전송을 위한 객체
📌질문 : 여기서 articleCommentRequest를 사용하는이유
articleCommentService.saveArticleComment(articleCommentRequest.toDto(UserAccountDto.of(
"uno", "pw", "uno@mail.com", null, null
)));
Entity는 절대 클라이언트에 노출시켜선 안된다.Entity에는 도메인 로직만 구현하고, View와 관련한 로직은 구현하지 않는다. 이기때문에??
✔️답변: 해당 컨트롤러의 응답을 독립적으로 작성하고자 했기 때문이다.
이렇게 함으로써 해당 응답을 사용하는 컨트롤러의 응답에 관한 요구사항변경 범위를 ArticleCommentRequest로 어느정도 가둘 수있다.
📌질문 : Repository 존재이유
Repository는 저장소라는 뜻을 가진 단어인데 , JPA에서 Repository 인터페이스를 생성하면 ,
JpaRepository<Entity, 기본키타입>을 상속받으면 CRUD가 자동으로 생성된다.
즉, JpaRepository를 사용하며, 데이터베이스에 crud의 명령을 실행하게 만드느 인터페이스
간단하게 crud명령을 데이터베이스에 전달하여 원하는 쿼리를 실행할 수 있다.
✔️답변: jpa기술을 사용해서 db에 접근하기 위해
package com.fastcampus.projectboard.controller;
import com.fastcampus.projectboard.dto.UserAccountDto;
import com.fastcampus.projectboard.dto.request.ArticleCommentRequest;
import com.fastcampus.projectboard.service.ArticleCommentService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RequiredArgsConstructor
@RequestMapping("/comments")
@Controller
public class ArticleCommentController {
private final ArticleCommentService articleCommentService;
@PostMapping ("/new")
//댓글등록
public String postNewArticleComment(ArticleCommentRequest articleCommentRequest) {
// TODO: 인증 정보를 넣어줘야 한다.
articleCommentService.saveArticleComment(articleCommentRequest.toDto(UserAccountDto.of(
"uno", "pw", "uno@mail.com", null, null
)));
return "redirect:/articles/" + articleCommentRequest.articleId();
}
@PostMapping ("/{commentId}/delete")
public String deleteArticleComment(@PathVariable Long commentId, Long articleId) {
articleCommentService.deleteArticleComment(commentId);
return "redirect:/articles/" + articleId;
}
}
callSuper
callSuper = true: 부모클래스 필드 값도 동일한지 체크.
callSuper = false: 본인클래스 필드 값만 고려.
테스트를 용이하게 하기 위해 추가함
테스트 목적 외에도,
repository 레이어에 영속되지 않은 객체는
메타 정보가 없는 것이 당연하므로
새로운 데이터를 만들 때 이 부분을 입력하지 않아도 되는
팩토리 메소드를 만들어주는 것이 의미가 있음
FormDataEncoder.java
form data 는 api 와는 다른 형태로 직렬화하여 사용해야 한다.
객체를 규약에 맞게 form data 포맷의 문자열로 바꿔주는 유틸리티를 테스트 전용으로 만들고, 테스트도 함께 작성
이게 없으면 `post().param()` 형태로 값을 넣을 수도 있지만 표현이 실제 post request 전송과 꼭 맞지 않는 듯 하고,
객체가 아니라 파라미터 단위로 넣어줘야 하므로 불편함.
비즈니스 로직에서는 필요없기때문에 테스트 전용도구로 등록
test목적으로 넣엇기 떄문에, test쪽에 있고 테스트 컴포넌트로 등록
objectMapper가 빈으로 인식해서 주입을 받을 수 있게끔 만듦
object를 받아서 multivalueMap -> 쿼리파라미터 부분만 추출해서 우리가 원하는 결과물을 내게끔 만들었다.
package com.fastcampus.projectboard.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import java.math.BigDecimal;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
@DisplayName("테스트 도구 - Form 데이터 인코더")
@Import({FormDataEncoder.class, ObjectMapper.class})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Void.class)
class FormDataEncoderTest {
private final FormDataEncoder formDataEncoder;
public FormDataEncoderTest(@Autowired FormDataEncoder formDataEncoder) {
this.formDataEncoder = formDataEncoder;
}
@DisplayName("객체를 넣으면, url encoding 된 form body data 형식의 문자열을 돌려준다.")
@Test
void givenObject_whenEncoding_thenReturnsFormEncodedString() {
// Given
TestObject obj = new TestObject(
"This 'is' \"test\" string.",
List.of("hello", "my", "friend").toString().replace(" ", ""),
String.join(",", "hello", "my", "friend"),
null,
1234,
3.14,
false,
BigDecimal.TEN,
TestEnum.THREE
);
// When
String result = formDataEncoder.encode(obj);
// Then
assertThat(result).isEqualTo(
"str=This%20'is'%20%22test%22%20string." +
"&listStr1=%5Bhello,my,friend%5D" +
"&listStr2=hello,my,friend" +
"&nullStr" +
"&number=1234" +
"&floatingNumber=3.14" +
"&bool=false" +
"&bigDecimal=10" +
"&testEnum=THREE"
);
}
record TestObject(
String str,
String listStr1,
String listStr2,
String nullStr,
Integer number,
Double floatingNumber,
Boolean bool,
BigDecimal bigDecimal,
TestEnum testEnum
) {}
enum TestEnum {
ONE, TWO, THREE
}
}
이 둘은 연관된 dependency
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
testImplementation 'org.springframework.security:spring-security-test'
WebMvcTest는 컨트롤러만 테스트를 한다. (슬라이스 테스트 ) 일부 꼭 필요한 컨트롤러와 빈들만 스캔
게다가 articleController만 스캔하도록 되어있기 때문에
다른 필요한 것들을 import로 꼭 넣어준다.
@Import({SecurityConfig.class, FormDataEncoder.class})
@WebMvcTest(ArticleController.class)
class ArticleControllerTest {
csrf
테스트나 각종 데모에서 발생할 수 있는 문제점을 우회화도록
데이터가 변경되는 작업에서는 csrf가 잇어야 테스트가 제대로 통과한다.
@RequiredArgsConstructor
롬복어노테이션
final이나 @NotNull이 붙은 필드의 생성자를 자동으로 생성해주어
새로운 필드를 추가할때 생성자를 새로 만들 필요가 없다.
@RequestMapping(value="" , method= )
요청이 들어왔을 때, 어떤 컨트롤러가 호출되어야하는 지 알려주는 지표
@PostMapping
@RequestMapping(value="" , method= )는 메소드 방식을 같이 적어줘야하지만
PostMapping이나 GetMapping은 값만 적어주기때문에 더 간결하다
@RequestMapping에서 value값을 안적어주면 GET 방식과 POST 방식을 모두 처리해준다.
하나의 url로 get과 post를 같이 처리해주고 싶다면 PostMapping이나 GetMapping를 같이 적어주면된다.
->댓글 구현 06:00
@PostMapping쓴 이유
현재 form은 get과 post만 다루게 되어있다. -> ( 폼태그에서 딜리트나 풋이 들어가야 할 상황은 없다. )
shouldHaveNoInteractions()
아무일도 하지않았다 .
'Spring' 카테고리의 다른 글
Spring boot 2.7.0 게시판 만들기 - 도메인, 뷰 (0) | 2022.11.19 |
---|---|
게시판 인증구현 - 뷰 (0) | 2022.11.13 |
게시판 페이지 기능구현 1-4 (0) | 2022.10.27 |
1)DataRestTest 클래스에서 통합테스트로 작성하는 이유 2) @Transactional을 붙이는 이유 (0) | 2022.10.20 |
톰캣 cmd에서 실행시키기 (0) | 2022.10.14 |