JAVA/JAVA | Spring 학습기록

[Spring] CRUD 기능 실습 중 @DeleteMapping 관련 에러

kth990303 2021. 5. 11. 19:59
반응형

저번에 포스팅한 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 문법을 검색하여 아래 블로그에서 도움을 얻었다.

velog.io/@max9106/Thymeleaf-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%AC%B8%EB%B2%95

 

[Thymeleaf] 자주 사용하는 문법

화면에 값을 출력할 때 사용조건문처럼 사용 한다. 해당 조건이 만족할 때만 보여준다.해당 value의 error가 있는 경우 출력한다.form의 validation error를 출력할 때 사용할 수 있다.form 태그 사용 시,

velog.io

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를 붙여주었다.

sas-study.tistory.com/348

 

[Spring Data JPA] No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call

JPA 관련 학습을 하는 도중에 일반적인 비즈니스 로직은 다 짰고 테스트로 돌려보고싶어서 테스트코드를 작성하는 도중에 다음과 같은 에러가 발생했다. No EntityManager with actual transaction availab

sas-study.tistory.com

 

@Transactional 어노테이션이 없으니 영속성 컨텍스트에 멤버를 보내지 못한 것이다.

여기까지 하여 나는 에러를 성공적으로 수정하였다.


후... 너무 힘들었다.

Thymeleaf 문법은 개인적으로는 나중에 배우고 싶다고 계속 미루고 미루다가 오늘 봉변을 당했다.

공부할게 참 많다~

 

 

반응형