반응형
컨트롤러 관련 테스트를 진행하다 Thymeleaf와 관련된 에러를 만났다.
1. 에러 코드
- 타임리프 관련 에러가 있다고 표시되고 있다.
jakarta.servlet.ServletException: Request processing failed: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [board/save], template might not exist or might not be accessible by any of the configured Template Resolvers
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [board/save], template might not exist or might not be accessible by any of the configured Template Resolvers
2. 에러가 발생한 상황
- 컨트롤러 코드
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/board")
@Controller
public class BoardController {
private final BoardService boardService;
@PostMapping("/save")
public void getBoard(@RequestBody BoardRequest request) {
boardService.saveBoard(request);
}
}
- 컨트롤러 테스트 코드
@Import(TestSecurityConfig.class)
@DisplayName("[Board] - 컨트롤러 테스트")
class BoardControllerTest extends ControllerTestSupport {
@Autowired private MockMvc mockMvc;
@Autowired private ObjectMapper objectMapper;
@MockBean private BoardRepository boardRepository;
@MockBean private BoardService boardService;
@DisplayName("실제 사용자가 게시글을 작성하고 저장하면 게시글이 저장된다.")
@Test
void test1() throws Exception {
//given
User user = createUser();
Board board = createBoard(user, "테스트 데이터");
BoardRequest request = BoardRequest.of(board);
//when
mockMvc.perform(post("/board/save")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk());
}
private Board createBoard(User user, String title) {
return Board.of(title,
"테스트",
10,
20,
user
);
}
private User createUser() {
return User.of(
"wlsdks12",
"wlsdks12",
"wlsdks",
"wlsdks12@naver.com",
RoleType.ADMIN,
UserStatus.Y
);
}
}
- 테스트 코드에 상속받은 서포터 코드
@Profile("test")
@Import(TestSecurityConfig.class) // 테스트 설정 클래스 적용
@WebMvcTest({
ChatMainController.class,
BoardController.class
})
public abstract class ControllerTestSupport {
}
- import받은 테스트 시큐리티 설정코드
- 테스트할때는 모든 보안설정의 접속을 허용해 줬다.
/**
* 테스트에 필요한 security관련 설정을 여기서 전부 처리하기위해 만들었다.
*/
@TestConfiguration
public class TestSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize ->
authorize.anyRequest().permitAll()
);
return http.build();
}
}
3. 문제 파악
- 일단 에러는 타임리프 관련이었고 곰곰히 생각해봤다.
- 처음 접근한곳은 컨트롤러다. 컨트롤러에는 @Controller가 달려있기 때문에 당연히 분명히 뷰를 찾을것이다.
- 근데 나는 void로 반환을 정했으니 당연히 thymeleaf관련 오류가 나는게 아닐까 생각했다.
4. 문제 해결
- @Controller 어노테이션을 사용하는 경우, 스프링은 기본적으로 뷰를 반환하려고 시도한다.
- 위의 코드에서는 void를 반환하므로 스프링은 URL 경로와 일치하는 뷰를 찾으려고 시도하게 된다.
- 이 경우 URL 경로가 /save이므로, 스프링은 save라는 이름의 뷰를 찾으려고 시도한다.
- 근데 해당 뷰가 실제로 없으면 위에서 본 오류가 발생하게 된다.
- API와 같이 뷰를 반환하지 않고 데이터만 반환하려는 경우, @RestController 어노테이션을 사용하면 된다.
- @RestController는 @Controller와 @ResponseBody를 합친 것으로, 반환 값이 HTTP 응답 본문에 직접 쓰여지게 된다.
- 따라서 위의 코드를 RESTful API로 사용하려면 다음과 같이 변경할 수 있다.
@RestController
public class YourControllerClass {
@PostMapping("/save")
public void getBoard(@RequestBody BoardRequest request) {
boardService.saveBoard(request);
}
}
- 또는, 기존의 @Controller를 유지하면서 메서드에 @ResponseBody를 추가하여 HTTP 응답 본문에 직접 값을 쓰도록 할 수도 있다.
@Controller
public class YourControllerClass {
@PostMapping("/save")
@ResponseBody
public void getBoard(@RequestBody BoardRequest request) {
boardService.saveBoard(request);
}
}
- 이러한 변경을 통해 뷰를 찾지 않고 HTTP 응답을 직접 제어할 수 있다.
나는 2번째 방법인 @ResponseBody를 메서드 위에 적어주고 테스트를 성공시켰다.
반응형
'Spring > Spring 오류해결' 카테고리의 다른 글
Spring Boot 3 및 Spring Batch 5에서 배치 테이블 자동 생성 문제 해결하기 (0) | 2023.11.23 |
---|---|
[스프링, 스프링부트] Spring - 게시글 삭제중 발생한 오류 해결 (0) | 2023.08.22 |