이 포스트는 김영한님의 ‘스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술’을 수강하고 작성하였습니다.

정적 컨텐츠

정적 컨텐츠는 서버에서의 별도 동작 없이 파일을 그대로 브라우저에 내려주는 것을 말한다.

스프링부트에서 정적 컨텐츠를 불러오는 과정

웹 브라우저1. localhost:8080/hello-static.html 리소스 요청내장톰캣 서버스프링 부트스프링 컨테이너2. hello-static 관련컨트롤러 확인3. hello-static 컨트롤러가없다면 static/ 확인static/hello-static.html4. 반환출처: 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한)

MVC와 템플릿 엔진

MVC = Model + View + Controller
템플릿 엔진은 HTML 파일을 그냥 브라우저에 전달하는 것이 아니라, 서버에서 별도의 동작을 한 후에 데이터를 동적으로 변환하여 전달한다.

스프링부트에서 템플릿 엔진을 이용한 요청 처리 과정

웹 브라우저1. localhost:8080/hello-mvc 요청내장톰캣 서버스프링 부트출처: 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한)스프링 컨테이너helloContainerviewResolver2. 매핑된 메서드 호출return: hello-mvcmodel(name: chicken)3. templates/hello-mvc.html(Thymeleaf 템플릿 엔진 처리)4. 변환 후 HTML 반환

API

API는 json과 같은 포맷으로 클라이언트에 전달하는 방식이다. 주로 서버 - 서버가 데이터를 주고 받을 때 사용한다.

@ResponseBody 동작 원리

웹 브라우저1. localhost:8080/hello-api 요청스프링 부트출처: 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한)스프링 컨테이너helloController내장톰캣 서버3. {name: cat} 형태로 응답JsonConverter: 기본 객체 처리StringConverter: 기본 문자 처리HttpMessageConverter2. @ResponseBody 라면message converter로
  • @ResponseBody를 사용하면
    • HTTP의 BODY에 문자 내용을 직접 반환
    • viewResolver 대신 HttpMessageConverter가 동작
    • 기본 문자 처리: StringHttpMessageConverter
    • 기본 객체 처리: MappingJackson2HttpMessageConverter
    • byte 처리 등등 여러 HttpMessageConverter가 기본으로 등록되어 있음

참고: 클라이언트의 HTTP Request의 accept 헤더와 서버의 컨트롤러 반환 타입을 조합해서 그에 맞는 HttpMessageConverter가 선택된다.

에러: Could not find acceptable representation

강의의 예제를 그대로 따라치면 재미없다는 생각이 있어서 강의 예제에 나온 코드를 살짝 비틀어서 아래와 같이 작성했는데, 바로 에러가 발생했다.

// 컨트롤러 내부에 작성
@GetMapping("hello-api")
@ResponseBody
public User helloAPI(@RequestParam(value = "name") String name) {
    User user = new User(name);
    return user;
}

static class User {
    private String name;

    public User(String name) {
        this.name = name;
    }
}

이런 식으로 코드를 작성했는데, 406 에러가 발생하며 Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation] 이런 메시지가 로그에 출력되었다. 응답 헤더에는 accept로 데이터의 형식을 지정해줄 수 있는데, accept에 지정되지 않은 데이터가 응답 메시지로 들어왔다는 것 같았다.

원인은 굉장히 간단했는데, User 클래스에 getter를 추가하지 않았기 때문이었다.

HttpMessageConverter에서 객체를 json으로 변환하는 과정에서 객체의 프로퍼티에 접근하려다보니 private이라 접근하지 못했고, 제대로 json으로 변환되지 않은 상태에서 응답으로 처리된 것 같았다.

그래서! 과연 그게 정답인지 실험을 하나 해봤다.

// 컨트롤러 내부에 작성
@GetMapping("hello-api")
@ResponseBody
public User helloAPI(@RequestParam(value = "name") String name) {
    User user = new User(name);
    return user;
}

static class User {
    public String name;

    public User(String name) {
        this.name = name;
    }
}

위에 있던 코드와 달라진 점으로는 User 클래스에 getter/setter가 사라지고 생성자만 남았다는 것과, namepublic이라는 것.
이대로 실행해봤는데, 결과는 당연히 제대로 json 형식으로 응답을 전해주고 있었다.

댓글남기기