저번에 포스팅한 CRUD 실습을 이어하던 중,
@DeleteMapping으로 삭제하려던 중 아래 사진과 같은 에러가 발생하였다.
This application has no explicit mapping for /error, so you are seeing this as a fallback.
1. 패키지 관계?
만약 스프링부트를 처음 실행했을 때 이 에러가 발생한다면 (또는 컨트롤러 메소드가 아무것도 실행이 안된다면)
위 에러는 보통 스프링부트 메인 클래스가 Controller, Service 등 스프링 빈들이 위치하고 있는 패키지의 상위 패키지에 없을 때 발생하는 에러라고 한다.
그러나 나는 @GetMapping은 실행이 잘되는 상황이기도 하고,
패키지 구조관계도 멀쩡했기 때문에 위와 같은 이유는 아니었다.
2. 최신 HTTP Method?
@DeleteMapping, @PutMapping 등의 최신 매핑기능을 이용하기 위해선 아래와 같은 코드를 작성해야 한다고 한다.
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
return new HiddenHttpMethodFilter();
}
따라서 나는 SpringBootApplication 클래스를 아래와 같이 수정하였다.
package shop210504.pracshop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@EnableJpaAuditing
@SpringBootApplication
public class PracshopApplication {
public static void main(String[] args) {
SpringApplication.run(PracshopApplication.class, args);
}
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
return new HiddenHttpMethodFilter();
}
}
이 코드가 없으면 @DeleteMapping, @PutMapping 등을 사용하지 못하므로
어느정도 정답에 가까운 해결방법이 됐다.
웬만한 에러는 이 정도면 해결이 되겠지만, 나는 저걸 추가해도 똑같은 에러가 떴다...
그럼 도대체 뭐가 문제일까?
3. Html 파일의 Thymeleaf 문법 미숙
처음에 나는 html파일 중 삭제부분을 아래와 같이 짰다.
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.tier}"></td>
<td><a href="/" th:href="@{'delete/'+${member.id}}">삭제</a></td>
</tr>
일단 <a href="/"> 부분에서 localhost:8080/members가 아닌 localhost:8080/ 만으로 이동될 뿐 아니라,
th:href= 로 작성하여 localhost:8080/delete/{id}로 url에 보내
없는 파일에 접속하여 위와 같은 에러가 뜨는 것이었다.
그러나 th:action으로 바꾼다 해도, th:method="delete"나 th:method="post"로 바꿔도 결과는 같았다...
진짜 너무 힘들었는데 thymeleaf 문법을 검색하여 아래 블로그에서 도움을 얻었다.
th:action
form 태그 사용 시, 해당 경로로 요청을 보낼 때 사용. (url)
form태그를 사용해야 하는구나... (사실 아직 타임리프 문법을 거의 모르고 막 부딪히면서 야생형으로 사용해보고 있는 입장이다... ㅠㅠ)
따라서 아래와 같이 바꿨다.
Index.html
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.tier}"></td>
<td>
<form id="deleteMember" th:action="@{'/members/' + ${member.id}}" method="post">
<input type="hidden" name="_method" value="delete"/>
<button id="delete-btn">삭제</button>
</form>
</td>
</tr>
IndexController
package shop210504.pracshop.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import shop210504.pracshop.domain.Member;
import shop210504.pracshop.service.MemberService;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class IndexController {
private final MemberService memberService;
@GetMapping("/")
public String home(Model model){
List<Member> memberList = memberService.showMembers();
model.addAttribute("members", memberList);
return "index";
}
@GetMapping("/members")
public String show(Model model){
List<Member> memberList = memberService.showMembers();
model.addAttribute("members", memberList);
return "index";
}
@DeleteMapping("/members/{id}")
public String delete(@PathVariable("id") Long id){
System.out.println("id = " + id);
memberService.delete(id);
return "redirect:/";
}
}
아예 "/"매핑을 추가하여 "/members" 매핑과 같은 기능을 하게 했다.
어차피 "/" 매핑은 나중에 메인홈페이지를 구체적으로 만들 시 꼭 필요하기도 했기 때문에 만들은 것이며,
현재는 메인홈페이지가 단순 회원멤버 리스트를 출력하고 있기 때문에 "/members"와 같이 만들었다.
이제 거의 다왔다.
사실 이정도만 하면 웬만한 99%의 개발자들은 해결됐을 것이다.
그러나 Spring Boot와 JPA조차 미숙했던 나는 한가지 실수를 더 했었다...
4. @Transactional
MemberServiceImpl.class와 MemberRepository.class에 @Transactional를 붙여주었다.
@Transactional 어노테이션이 없으니 영속성 컨텍스트에 멤버를 보내지 못한 것이다.
여기까지 하여 나는 에러를 성공적으로 수정하였다.
후... 너무 힘들었다.
Thymeleaf 문법은 개인적으로는 나중에 배우고 싶다고 계속 미루고 미루다가 오늘 봉변을 당했다.
공부할게 참 많다~
'JAVA > JAVA | Spring 학습기록' 카테고리의 다른 글
[Spring] 스프링이 태어난 이유_서블릿, JSP로 만든 MVC의 한계 (0) | 2021.07.06 |
---|---|
[Spring] lombok의 @Builder와 JpaRepository를 도입해보았다 (0) | 2021.05.17 |
[Spring] MySQL+JPA+Spring+Gradle 회원조회 Read 실습 (0) | 2021.05.10 |
[Spring] Spring boot Test 코드 작성해보기 (TDD 연습) (2) | 2021.05.05 |
[Spring] MySQL+JPA+Spring+Gradle 환경설정 및 실습 (0) | 2021.05.04 |