View Tempate Engine: JSP 와 Thymeleaf

 

1. JSP 사용

Embedded Sevlet 컨테이너의 Spring Boot를 사용할 경우 jar 를 사용할 수 없기 때문에 war 방식을 선택해야 한다.

 WAR(Web Archive) 는 웹 어플리케이션 압축 타입으로 Servlet(JSP) 관련 패키지들을 포함하고 있기 때문에 복잡하고 무거운 구조이다.

반면에 Spring Boot는 독립적이고 가벼운 실행을 목표로 하기 때문에 이 방법과는 맞지 않아서, JSP 사용을 권장하지 않는다. 그 대체 템플릿 엔진으로 나온 것이 바로 Thymeleaf

 

[JSP 사용방법 업데이트 예정]

 

2.Thymeleaf

: 템플릿 엔진으로서, 기존 HTML 코드와 구조를 변경하지 않고 덧붙이는 방식이 특징

 

- view와 관련된 설정들은 꼭 정해진 디렉토리에 위치해있어야 한다. 

/src/main/resources/templates

 

- 필요한 dependency (maven 기준) - thymeleaf 만 추가해주었다.

 

 

Maven Repository : https://mvnrepository.com/

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

 

Thymeleaf 제공 Template

  • HTML
  • XML
  • TEXT
  • Javascript
  • Css
  • Raw

 

Thymeleaf 문법

- th:text="${변수명}"

<!--index.html-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>title</title>
</head>
<body>
<!--/*@thymesVar id="name" type=""*/-->
<h1 th:text="${name}">Hello Thymeleaf</h1>
</body>
</html>

- th:href="@{변수명}" 

: <a> 태그의 하이퍼링크 속성과 동일, 이동하고자 하는 페이지가 있을 때 사용

 

- th:with="${변수명}"

: 변수 형태의 값을 재정의하는 속성, 새 변수값 생성용

 

- th:value="${변수명}"

: input의 value에 값을 삽입할 때 사용, + 기호로 여러개의 값을 넣을 수 있음

 

Javascript에서 사용시 

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <script th:inline="javascript">
        /*<![CDATA[*/
        let data = /*[[${data}]]"*/;
        /*]]*/
    </script>
</head>
<body>
</body>
</html>

 

Controller 코드 예시

1. [Model] 사용 ver

@Controller
public class TempController {

    @GetMapping("/index")
    public String test(Model model){
        //속성 이름과 속성값 model에 추가 -> index.html 페이지에서 {$name} 으로 값을 읽음
        model.addAttribute("name","yoon");
        //리턴할 페이지 이름
        return "index";
    }
}

2. [ModelAndView] 사용 ver

@GetMapping("/test")
public ModelAndView test(HttpServletRequest request){
 	....
    ModelAndView mv = new ModelAndView();
    //리턴할 페이지 
    mv.setViewName("test");
    //리턴할 속성과 객체
    mv.addObject("request",request);

    return mv;
}

 

* Model 과 ModelAndView 차이

https://bestkingit.tistory.com/155

 

[Spring Boot] Model 과 ModelAndView 차이.

인터셉터에서 세션 처리를 하는 도중에 문제가 생겼다. 지금까지 Controller에서 Model을 이용하여 view에 넣어주었는데, HandlerInterceptor의 postHandle에는 ModelAndView를 이용하는 것이다..(확장하면 되지..

bestkingit.tistory.com

 

'Web > Spring' 카테고리의 다른 글

Spring - AOP  (0) 2022.01.07
Spring - IoC/DI 란?  (0) 2022.01.07
Spring Boot 시작하기 1장 (Get API-@GetMapping)  (0) 2022.01.07

AOP (Aspect Oriented Programming)

관점지향 프로그램

- 스프링 어플리케이션은 대부분 특별한 경우를 제외하고는 MVC 웹 어플리케이션에서는 Web Layer, Business Layer, Data Layer로 정의

 

- Web Layer: REST API를 제공하며, Client 중심의 로직 적용

- Business Layer: 내부 정책에 따른 logic을 개발하며, 주로 해당 부분을 개발 

- Data Layer: 데이터 베이스 및 외부와의 연동을 처리

 

횡단 관심 

- Method Parameter Log - 메소드 실행 시 어떤값이 들어가고 리턴되는지 확인 

- 실행시간 Log - 특정 메소드의 실행시간

- Parameter Encode : 메소드가 들어갈 때나 반환될 때 값을 변환시켜줌

 

주요 Annotation

@Aspect : 자바에서 널리 사용하는 AOP프레임워크에 포함되며, AOP를 정의하는 Class에 할당

@Pointcut : 기능을 어디에 적용시킬지, 메소드? Annotation?등 AOP를 적용시킬 지점을 설정

@Before : 메소드를 실행하기 이전

@After : 메소드가 성공적으로 실행 후, 예외가 발생되더라도 실행

@AfterReturning : 메소드 호출 성공 실행 시 (Not Throws)

@AfterThrowing : 메소드 호출 실패 예외 발생 (Throws)

@Around : Before/after 모두 제어

 

'Web > Spring' 카테고리의 다른 글

Spring Boot JSP & Thymeleaf  (0) 2022.06.08
Spring - IoC/DI 란?  (0) 2022.01.07
Spring Boot 시작하기 1장 (Get API-@GetMapping)  (0) 2022.01.07

저작자: PHẠM HUY HO&amp;amp;Agrave;NG 이미지 출처 :&amp;amp;nbsp;https://toidicodedao.com/2015/11/03/dependency-injection-va-inversion-of-control-phan-1-dinh-nghia/

 

 

DI (Dependency Injection): 의존성 주입

- 외부의 컨테이너(Spring Conainer)에서 객체를 생성한 후 의존성을 주입시키는 설계 패턴

- 스프링에서 객체는 Bean 으로 표현된다. 

 

DI 사용의 장점

- 모듈 간의 결합도가 낮아져, 유연성이 높아짐 따라서 안정적인 테스트 가능 ( Mock 을 통해)

- *순환참조(Circular Reference) 제거

 

*순환참조(Circular Reference) : 참조하는 대상이 서로 몰려있어서 참조할 수 없게 되는 현상.

- 재사용이 높고, 코드 변경 및 확장의 영향이 적음 (추상화)

 

 

 

DI 사용 - 객체에 의존성을 주입시키는 방법

대표적으로 3가지 방법이 있다. 

[세터 기반 의존성 주입, 생성자 기반 의존성 주입, 필드 기반 의존성 주입]

 

 

 

 

 

1) 세터 기반 의존성 주입

- setter()메소드에 @Autowired를 지정하여 객체에 대한 의존성을 주입한다.

*@Autowired 란? 

- 자동연결 (Autowiring)을 뜻하며 스프링 DI에서 사용되는 어노테이션으로, 컨테이너에 빈을 자동으로 주입해준다(즉, @Autowired 가 붙은 필드에 자동적으로 의존성을 주입해줌).

- 변수, 생성자, Setter(), 일반 메소드에 적용가능하다

 

예시: StudentController.java 에 Student 객체를  setter()를 사용하여 의존성을 주입해보자

public class StudentController {
    private Student student; //의존성 주입 대상 필드

    //Setter 를 사용한 의존성 주입
    @Autowired
    public void setStudent(Student student){
        this.student = student;
    }
}

이 주입 방법은 순환 참조 및 결합도 (Coupling) 문제로 권장되지는 않는다.

 

 

 

 

 

2) 생성자 기반 의존성 주입

- 생성자에 @Autowired 애너테이션을 지정하여 객체에 의존성을 주입한다

- 세터기반의존성 주입에 비해 권장되는 방식이다.

예시: StudentController.java 에 Student 객체를 생성자를 사용하여 의존성을 주입해보자

public class StudentController {
    private Student student; //의존성 주입 대상 필드

    //생성자를 사용하여 의존성 주입
    @Autowired
    public StudentController(Student student){
        this.student = student;
    }
}

 

Lombok을 사용한다면 @RequiredArgsConstructor를 사용한다면 위와 같은 코드를 조금 더 간결하게 구현할 수 있다.

다만 의존성 주입 대상 필드는 꼭 final로 선언을 해주어야만 해당 필드를 가진 생성자가 만들어진다.

public class StudentController {
    private final Student student; //의존성 주입 대상 필드
}

 

 

 

 

 

3) 필드 기반 의존성 주입

단순히 필드에 @Autowired 를 붙여서 의존성을 주입하는 것

예시: StudentController.java 안에서 Student 객체(필드) 의존성을 주입해보자

public class StudentController {
    @Autowired
    private Student student; //의존성 주입 대상 필드
}

 


 

IoC(Inversion Of Control) : 제어의 역전

- 제어의 역전, 즉 제어권한이 뒤바뀌었다는 뜻 (권한이 개발자에서 프레임워크로)

- 예전 자바기반 어플리케이션은 개발자가 직접 자바 객체를 생성하고, 의존관계를 연결함 (제어권을 개발자가 가지고 있었음 => DI)

하지만, Servlet 과 EJB가 생겨나면서 객체의 생성/관리등의 제어권한이 외부의 컨테이너로 넘어가 뒤바뀌게 됨 (제어권을 컨테이너가 가지게됨) 이를 우리는 "제어의 역전", IoC라고 표현한다.

 

예시) 개발자가 직접 객체를 생성하는 코드

import BBB.BBB;

public class AAA {
    
    private BBB bbb;
    
    public AAA(){
        bbb = new BBB(); // AAA 객체안에서 BBB 라는 객체가 New 생성자로 직접생성됨
    }
}

기존에 객체를 생성했던 방법을 간단히 살펴보면, 위의 코드와 같이 객체 AAA안에서 BBB의 객체를 쓰려면 new BBB()로 직접 생성해야 했다(즉 개발자들이 직접 객체를 생성해서 사용해옴).

 

이 코드를 다시 해석하면 AAA 라는 객체는 BBB라는 객체를 사용(의존)하고 있는 것을 확인할 수 있는데, 이것을 IoC 의 형태로 바꿔서 쓰면 (의존성 주입) 다음과 같다.

 

예시) 컨테이너에 의한 객체를 사용하는 코드

import BBB.BBB;
import org.springframework.beans.factory.annotation.Autowired;

public class AAA {

    @Autowired
    private BBB bbb;

}

이 객체는 스프링 컨테이너에 의해 생성 및 관리되기 때문에 코드에서 직접 객체를 생성하지 않고 의존관계만 주입해주는 것을 확인할 수 있다. 이것을 우리는 제어가 역전되었다 (IoC) 라고 한다. 이를 간단하게 말하면,

'의존성을 주입해준다' = 외부(스프링 컨테이너)에서 객체의 레퍼런스(객체의 주소)를 전달하여 객체를 참조할 수 있게 한다.

 

 

따라서 스프링에서 객체가 만들어지고 실행되는 과정은 다음과 같다

객체 생성 -> 객체에 의존성 주입 (컨테이너에 의해 관리되어지는 객체) -> 의존성 주입된 객체의 메소드 호출 

 

 

 

IoC와 DI의 차이점

DI는 IoC 모델을 구현하는 방식중에 하나고,

 IoC(객체의 제어권한을 외부에서 관리)는 DI(외부에서 생성된 객체를 주입하는 것)를 통해 실현되는 것이라고 이해하면 될 것 같다. 큰 차이점은 없는 것으로 보인다.

 

References

https://velog.io/@gillog/Spring-DIDependency-Injection

https://junu0516.tistory.com/87

https://life-with-coding.tistory.com/433

'Web > Spring' 카테고리의 다른 글

Spring Boot JSP & Thymeleaf  (0) 2022.06.08
Spring - AOP  (0) 2022.01.07
Spring Boot 시작하기 1장 (Get API-@GetMapping)  (0) 2022.01.07

Spring 은 엔터프라이즈 애플리케이션 개발에 널리 사용되는 오픈 소스 경량 프레임워크이고, Spring Boot는 기존의 Spring 프레임워크 위에 구축되어 REST API 개발에 널리 사용되는  오픈 소스 마이크로 프레임워크이다. 

 

Spring Boot 의 장점

- Spring 기반 어플리케이션의 빠르고 쉬운 개발 

- war 파일의 배포가 필요 없음

- 독립 실행형 어플리케이션 (standalone application)

- Tomcat, Jetty, Undertow 같은 웹서버를 응용프로그램에 직접 내장할 수 있음

- XML을 구성할 필요가 없음

- 소스코드의 양 감소

- 간단할 설정 및 관리, 여러 기능의 추가가 쉬움


이제 Spring Boot 프로젝트를 만들어

GET, POST, PUT, DELETE 방식의 웹 서비스(Rest API)의 사용방법을 익혀보겠다.

 

준비과정

난 IntelliJ 가 커뮤니티 버전이라, Spring Initializr 로 프로젝트를 다운받아 사용해야 한다.

Spring Initializr : https://start.spring.io

Spring Initializr 에서 Gradle - Java- Spring Boot(2.6.2) -  Dependencies [Spring Web] 을 선택한 후 zip 파일을 다운 받아, IntelliJ에서 열어주었다.

 


1. Get API 

프로젝트 실행 - main 아래 [controller] 패키지 생성 - 패키지 안에 [GetAPIController.java] 생성

 

[GET 예제 코드 - 1]

1. @RestController 

2. @RequestMapping("/uri") // 원하는 이름으로 지정해준다.

3. Get을 확인할 테스트 메소드(public String testGetMapping()) 만들기  

4. @GetMapping(path="/uri2") // 원하는 이름으로 지정해준다.

5. 프로젝트 Run

6. Talend API tester (Google Chrome extension) 에서 GET 으로 확인해보기 

성공적으로 상태코드 200과 함께 리턴된 메세지를 확인할 수 있었다.

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

	@GetMapping(path = "/test") // 3. GetMapping 에 path 추가, http://localhost:8080/demo/test
	public String testGetMapping(){
    return "test get mapping";
	}
}

같은 코드지만 다른 방식으로는 @RequestMapping(path="", method = RequestMethod.GET)으로 써줄 수 도 있다.

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

	@RequestMapping(path="/test", method= RequestMethod.GET)
	public String testGetMapping2(){
    return "test get mapping";
	}
}

[GET 예제 코드 2- @PathVariable]

일관적이지 않은 URI일 경우에 우리는 @PathVariable 을 사용한다.

예를 들어 사용자 아이디같은 경우라면

 /demo/test/user1

 /demo/test/user2 .. 처럼 사용자 아이디에 따라 계속 바뀌기에 우리는 고정된 URI를 사용할 수 없다. 

이런 경우에 @PathVariable 코드를 써주면 된다.

package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

    @GetMapping("/test/{userID}") //3. {변화하는 값}
    public String pathVariable(@PathVariable String userID){
        return userID;
    }
}

/test/{userID} 에 user100을 넣어주었고 Get이 정상적으로 동작하여 user100이 return 된 것을 Body에서 확인할 수 있다. 

 


[GET 예제 코드 3- @RequestParam]

 

http://localhost:8080/demo/query?userId=100&name=ellie

이런식의 주소를 본 적이 있을 것이다.

?를 기준으로 key=value&key=value로 이어진 형태로, 이런 경우에는 @RequestParam을 활용하면 된다. 

 

방법 1 - Map 사용

import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

//http://localhost:8080/demo/query?userId=100&name=ellie
//방법1. Map 사용    
@GetMapping(path = "test1")
    public String queryTest1(@RequestParam Map<String,String> queryTest){
    
    StringBuilder sb = new StringBuilder();
    queryTest.entrySet().forEach( entry -> {
        System.out.println(entry.getKey());
        System.out.println(entry.getValue());
        System.out.println("\n");
        
        sb.append(entry.getKey()+" = "+entry.getValue());
    });
}

 

방법2 - 각 변수에  @RequestParam 붙이기

package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

    //방법 2 : 각 변수를 명시해주기 (사용해주는 Key 값을 알기위해서)
    @GetMapping("test2")
    public String queryTest2(
            @RequestParam String userID,
            @RequestParam String name
    ){
        return userID+" " +name;
    }
}

 

방법3 - 클래스 따로 만들기

package com.example.demo.controller;

import com.example.demo.User;
import org.springframework.web.bind.annotation.*;

@RestController // 1. RestController 추가
@RequestMapping("/demo") //2. RequestMapping 으로 시작 Path 지정
public class GetAPIController {

    //방법 3: 변수가 많아, 외부 클래스를 따로 만들어 불러오기
    @GetMapping("test3")
    public String queryTest3(User user){
        return user.toString();
    }
}

Talend API tester에 아래와 같이 Query Parameters 를 추가하고 Send 를 하면 성공적으로 값을 받아온 것을 확인할 수 있다. 

 

 

 

References

https://bambooagile.eu/insights/pros-and-cons-of-using-spring-boot/

https://mangkyu.tistory.com/49

'Web > Spring' 카테고리의 다른 글

Spring Boot JSP & Thymeleaf  (0) 2022.06.08
Spring - AOP  (0) 2022.01.07
Spring - IoC/DI 란?  (0) 2022.01.07

+ Recent posts