[Spring 입문] 시작 2 (Day2-0204)

왕감자·2025년 2월 4일

2025 내일배움캠프

목록 보기
2/39
post-thumbnail

Spring MVC

MVC 패턴

  • 코드의 재사용성과 유지보수성을 높이고, 개발자들 간의 협업을 용이하게 함
  • 소프트웨어를 개발할 때 MVC 패턴을 적용하여 구조를 잘 설계하는 것이 중요

Spring MVC

Spring에서 MVC 디자인 패턴을 적용하여 HTTP 요청을 효율적으로 처리

  • 중앙에 있는 DispatcherServlet이 요청을 처리하기 위한 공유 알고리즘으 제공하는 Front Controller 패턴을 중심으로 설계

Servlet(서블릿)

  • 자바를 사용하여 웹 페이지를 동적으로 생성하는 서버 측 프로그램 혹은 그 사양
  • HTTP를 분석해서 그 안의 데이터들을 HttpServletRequest 객체에 넣어줌

Front Controller 패턴

Spring은 DispatcherServlet을 사용하여 Front Controller 패턴 방식으로 API 요청을 효율적으로 처리




@Controller / @ResponseBody

@Controller
public class HelloController {

    @GetMapping("/api/hello")
    public String hello() {
        return "html파일";
    }
}

➡ String반환 시 html 파일 찾음


@Controller
public class HelloController {

    @GetMapping("/api/hello")
    @ResponseBody
    public String hello() {
        return "Hello World";
    }
}

@ResponseBody 를 붙이면 문자열 반환


@RequestMapping

중복되는 API 경로 설정

@Controller
@RequestMapping("/api")
public class HelloController {
	
    // "/api/get"
    @GetMapping("/get") 
    @ResponseBody
    public String get() {
        return "GET Method 요청";
    }
	
    // "/api/post"
    @PostMapping("/post")
    @ResponseBody
    public String post() {
        return "POST Method 요청";
    }
}


정적 페이지

[resources > static > hello.html]

@GetMapping("/static-hello")
public String hello() {
    return "hello.html";
}
// http://localhost:8080/static-hello > 안 뜸
// http://localhost:8080/hello.html > 뜸

정적페이지는 굳이 컨트롤러를 거쳐서 반환할 필요가 없음 ➔ 바로 html을 호출하면 됨

/static-hello가 안 되는 이유
thymeleaf 설정을 해두면 컨트롤러에서 html을 찾을 때 templates 폴더 내부에서 찾음
설정을 지우면 http://localhost:8080/static-hello 이 경로로 들어가도 hello.html이 뜸
➔ static 폴더에서 찾음


💡 static 폴더의 html에 접근하는 방법

  1. URL 경로에서 직접 접근 (thymeleaf 엔진이 걸려있지 않은 상황에서)
  2. redirect (thymeleaf 엔진이 걸려있고 컨트롤러를 거쳐야 할 때)
@GetMapping("/html/redirect")
public String htmlStatic() {
    return "redirect:/hello.html";
}

http://localhost:8080/html/redirect



[resource > templates > hello.html]

@GetMapping("/html/templates")
public String htmlTemplates() {
    return "hello"; // .html을 안 붙여도 templates 폴더에서 찾음
}

동적 페이지

@Controller
public class HtmlController {

    private static long visitCount = 0;

    @GetMapping("/html/dynamic")
    public String htmlDynamic(Model model) {
        visitCount++;
        model.addAttribute("visits", visitCount);
        return "hello-visit";
    }
}
<div>
    (방문자 수: <span th:text="${visits}"></span>)
</div>



데이터를 Client에 반환

@Controller
@RequestMapping("/response")
public class ResponseController {

	// [Response header] ➔ Content-Type: text/html
	// [Response Body] ➔ {"name":"Robbie", "age":95}
    
	@GetMapping("/json/string")	
	@ResponseBody
    public String helloStringJson() {
        return "{\"name\":\"Robbie\",\"age\":95}"; // Json 형태인 String
    }
}

text/html 형태로 날아감

Json은 자바의 언어가 아님


@Controller
@RequestMapping("/response")
public class ResponseController {

    // [Response header] ➔ Content-Type: application/json
    // [Response Body] ➔ {"name":"Robbie", "age":95}

    @GetMapping("/json/class")
    @ResponseBody
    public Star helloClassJson() {
       return new Star("Robbie", 95);
    }
}

application/json 형태로 날아감

자바의 언어(객체) - Spring에서 자바의 객체를 자동으로 json으로 변환



@RestController

메서드에 @ResponseBody를 명시하지 않아도 자동 적용됨

@RestController
@RequestMapping("/response/rest")
public class ResponseRestController {

    @GetMapping("/json/string")
    public String helloStringJson() {
        return "{\"name\":\"Robbie\",\"age\":95}";
    }

    @GetMapping("/json/class")
    public Star helloClassJson() {
        return new Star("Robbie", 95);
    }
}



Jackson

Json 데이터 구조를 처리해주는 라이브러리

  • 자바 객체(Object)를 Json타입의 String으로 변환
  • Json타입의 String을 Object로 변환

[객체 ➞ JSON]

@Test
@DisplayName("Object To JSON : get Method 필요")
void test1() throws JsonProcessingException {
    Star star = new Star("Robbie", 95);

    ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
    String json = objectMapper.writeValueAsString(star);

    System.out.println("json = " + json);
}

💡 get 메서드 필요

@Getter // ⭐
public class Star {
    String name;
    int age;

    public Star(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Star() {}
}

[JSON ➞ 객체]

@Test
@DisplayName("JSON To Object : 기본 생성자 & (get OR set) Method 필요")
void test2() throws JsonProcessingException {
    String json = "{\"name\":\"Robbie\",\"age\":95}";

    ObjectMapper objectMapper = new ObjectMapper();
    Star star = objectMapper.readValue(json, Star.class);

    System.out.println("star.getName() = " + star.getName());
}

💡 기본생성자 & get OR set 메서드 필요

@Getter // ⭐
public class Star {
    String name;
    int age;

    public Star(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Star() {}  // ⭐
}



Path Variable

서버에 보내려는 데이터를 URL 경로에 추가

@GetMapping("/star/{name}/age/{age}")
@ResponseBody
public String helloRequestPath(@PathVariable String name, @PathVariable int age) {
    return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
}


Request Param

서버에 보내려는 데이터를 URL 쿼리 스트링에 추가

[GET]

@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}


[POST]

// Header - Content Type: application/x-www-form-urlencoded
// Body - name=Robbie&age=95
@PostMapping("/form/param")
@ResponseBody
public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}


public String helloGetRequestParam(@RequestParam String name, int age)
@RequestParam을 다 붙이지 않고 한 번만 붙여도 됨 ➞ but 값이 없으면 X


🤔 값이 있을 수도 있고 없을 수도 있다면? ➡ (required = false)
public String helloGetRequestParam(@RequestParam(required = false)String name, int age)




HTTP 데이터를 객체로 처리하는 방법

1) @ModelAttribute : Body에 들어온 쿼리스트링 방식의 데이터를 객체에 맵핑해서 가져올 수 있음

public class Star {

    String name;
    int age;

    public Star(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
    // [Request Sample]
    // GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
    @GetMapping("/form/param/model")
    @ResponseBody
    public String helloRequestParam(@ModelAttribute Star star) {
        return String.format("Hello, @ModelAttribute.<br> (name = %s, age= %d) ", star.name, star.age);
    }
  • @ModelAttribute를 사용해서 객체로 데이터를 받아올 때, Setter 혹은 오버로딩 된 생성자 꼭 필요

💡 @ModelAttribute 생략 가능

public String helloRequestParam(Star star)

@RequestParam도 생략 가능한데, 그럼 생략을 했을 때 Spring은 이게 뭔지 어떻게 구분함?
➡ 파라미터가 Simple value 타입이면 @RequestParam, 아니라면 @ModelAttribute로 간주


2) @RequestBody : HTTP 요청의 본문(JSON, XML 등)을 자바 객체로 변환

    // [Request Sample]
    // POST http://localhost:8080/hello/request/form/json
    // Header - Content Type: application/json
    // Body - {"name":"Robbie", "age":"95"}
    @PostMapping("/form/json")
    @ResponseBody
    public String helloPostRequestJson(@RequestBody Star star) {
        return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
    }



DTO (Data Transfer Object)

데이터 전송 및 이동을 위해 생성되는 객체

Entity

데이터베이스 테이블과 매핑되는 객체

  • Spring Boot나 JPA를 사용할 때 DB의 한 개의 행(Row)을 객체로 다룰 수 있도록 하는 클래스

0개의 댓글