반응형

1. 개요

 - Argument Resolver 란?

 - 예제

 

2. Argument Resolver 란?

 - Controller로 들어온 파라미터를 가공하거나 수정 기능을 제공하는 객체이다. 교재에서는 이를 사용해 Controller로 들어온 특정 파라미터에 세션 정보를 가공하여 넣어주었다.

 - Argument Resolver를 Controller 단에서 사용하면 중복 코드(HttpSession에서 세션 로드, HttpServletRequest에서 요청 url 및 ip 정보 로드 등)를 깔끔하게 처리할 수 있다. > 예제를 보면 이 말의 의미를 알 수 있다.

 - 예제에서는 LoginUser 어노테이션이 붙은 SessionUser 객체에 대해 Argument Resolver를 설정해주었다.

 

3. 예제

 3.1. @LoginUser.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ssk.springboot.config.auth;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import java.lang.annotation.RetentionPolicy;
 
/*
 * Target : 어노테이션이 생성될 수 있는 위치 지정. PARAMETER면 메소드의 파라미터로 선언된 객체에서만 사용 가능
 * Retention : 어노테이션의 메모리 생명 주기 지정. 런타임에도 해당 어노테이션 객체는 메모리에 올라가있음
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
 
}
cs

 - 파라미터 단위에서 사용 가능하며, 런타임 시 메모리에 적재되는 LoginUser 어노테이션을 생성한다.

 - 어노테이션을 만들어 주는 이유는 이 어노테이션이 붙은 파라미터에 대해 Argument Resolver 를 설정하기 위함이다.

 

 3.2. LoginUserArgumentResolver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.ssk.springboot.config.auth;
 
import javax.servlet.http.HttpSession;
 
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
 
import com.ssk.springboot.config.auth.dto.SessionUser;
 
import lombok.RequiredArgsConstructor;
 
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver{
 
    private final HttpSession httpSession;
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class!= null;
        boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
        return isLoginUserAnnotation && isUserClass;
    }
 
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // TODO Auto-generated method stub
        return httpSession.getAttribute("user");
    }
}
cs

 - HandlerMethodArgumentResolver 인터페이스를 상속받는 구현체 클래스를 생성한다.

 - 이 인터페이스는 개발자가 커스텀하는 Argument Resolver에 대한 여러 메서드를 지원한다.

 - supportsParameter 메서드는 들어온 파라미터에 대해 resolveArgument 메서드를 실행할지 말지 판단한다. 리턴 값이 true면 결과적으로 resolveArgument 메서드를 실행하게 되는데, 이 메서드는 파라미터를 가공하는 역할을 한다.

 

 - parameter.getParameterAnnotation(LoginUser.class) != null, 즉, 해당 파라미터의 어노테이션이 LoginUser이고,

   parameter.getParameterType이 SessionUser일 경우 true를 반환하고, resolveArgument 메서드는 HttpSession. getAttribute("user") 를 반환한다.

 

 - 정리하면, 컨트롤러의 파라미터 중 @LoginUser 어노테이션이 붙어있는 SessionUser 객체가 있을 경우 해당 파라미터에 user 세션 정보를 리턴한다.

 

 3.3. Controller.java

1
2
3
4
5
6
7
8
9
10
    @GetMapping("/")
    public String index(Model model, @LoginUser SessionUser user) {
        
        model.addAttribute("posts",postsService.findAllDesc());
        if(user != null) {
            System.out.println(user.getName());
            model.addAttribute("userName", user.getName());
        }
        return "index";
    }
cs

 - 파라미터로 들어온 user 객체에 HttpSession.getAttributes("user")에 해당하는 값이 들어있음을 확인할 수 있다.

반응형
반응형

* 이동욱 저자의 스프링 부트와 AWS로 혼자 구현하는 웹서비스 교재 참고

1. 개요

 - JPA란?

 - H2란?

 - JPA 및 H2 설정 및 연동

 - Junit을 사용한 JPA CRUD 테스트

 

2. JPA란?

 - DB 처리를 쿼리 매핑(ex: ibatis, mybatis)이 아닌 객체 매핑으로 처리할 수 있도록 하는 기술

 - ORM (Object Relational Mapping) 기반

 - 기본적인 CRUD(Create, Read, Update, Delete) 메서드 자동 생성

 

3. H2란?

 - 인메모리 관계형 데이터베이스

 - 별도의 설치가 필요 없이 프로젝트 의존성만으로 관리가 가능

 - 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때마다 초기화됨 (테스트용으로 많이 사용)

 

4. JPA 및 H2 의존성 설정 (build.gradle)

1
2
compile('org.springframework.boot:spring-boot-starter-data-jpa'//스프링 부트용 Spring Data Jpa 라이브러리
compile('com.h2database:h2'//h2 라이브러리
cs

 

5. JPA 설정 및 예제

 5.1. 도메인 설정 (Posts.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Getter
@NoArgsConstructor
@Entity
public class Posts{
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(length = 500, nullable = false)
    private String title;
    
    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;
    
    private String author;
    
    @Builder
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}
cs

- @NoArgsConstructor : 클래스에 대한 기본 생성자 메서드 생성

- @Entity : 테이블과 링크될 클래스를 지정하는 어노테이션이며, 언더스코어 네이밍으로 테이블 이름을 매칭

  (ex : PostsManager > posts_manager table)

 

- @Id : PK 필드

 

- @GenerateValue(strategy = GenerationType.IDENTITY) : 자동 인덱스 생성 설정

 

- @Column : 테이블의 컬럼을 나타내며 필요 옵션 추가 시 length, nullable, columnDefinition 등을 사용

  > null을 허용하지 않고 길이가 500인 title 컬럼을 생성 

  > null을 허용하지 않고 TEXT 타입인 content 컬럼을 생성

  > 필드에 Column 어노테이션을 붙이지 않아도 기본적으로 컬럼이 생성됨

 

- @Builder : 해당 클래스의 빌더 패턴 클래스 생성

  > build 패턴 사용시 보다 명시적인 객체 생성이 가능 (PostTestRepository.java 코드의 21번째 줄 참고)

 

 5.2. JpaRepository 설정 (PostsRepository.java)

1
2
3
public interface PostsRepository extends JpaRepository<Posts, Long>//Entity 클래스, PK 타입
 
}
cs

- extends JpaRepository<Entity.class, PK Type> : 기본적인 CRUD 메서드를 지원하는 Repository 인터페이스 생성

 > JpaRepository : DB Layer 접근자를 의미

 > Entity 클래스와 해당 Entity Repository는 같은 패키지에 위치해야함.

 

6. H2 설정 (src/main/resources/application.properties)

1
2
3
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.h2.console.enabled=true
cs

- spring.jpa.show-sql=true : 콘솔 내에서 쿼리 로그를 확인할 수 있도록 하는 설정

- spring.jpa.properties.hibernate.dialect : H2 형태의 쿼리 로그를 MySQL 버전으로 변경

- spring.h2.console.enabled=true : h2 웹 콘솔 사용 허용 (localhost:port/h2-console 으로 접속 가능)

 

7. Junit 테스트 (PostsRepositoryTest.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@RunWith(SpringRunner.class)
@SpringBootTest //별다른 설정없이 SpringBootTest를 사용할 경우 H2 데이터 베이스를 자동으로 실행
public class PostsRepositoryTest {
 
    @Autowired
    PostsRepository postsRepository;
    
    //After : 테스트 단위가 끝날때마다 수행되는 메서드를 지정하는 어노테이션
    @After
    public void cleanup() {
        postsRepository.deleteAll();
    }
    
    @Test
    public void 게시글저장_불러오기() {
        
        String title = "테스트 게시글";
        String content = "테스트 본문";
        
        //builder 클래스를 통해 생성자 생성 후 save (insert/update)
        postsRepository.save(Posts.builder()
                                .title(title)
                                .content(content)
                                .author("sksim@gmail.com")
                                .build());
        
        List<Posts> postsList = postsRepository.findAll();
        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}
cs

- @SpringBootTest : H2 데이터 베이스 자동 실행 

 

- postsRepository.save() : posts 테이블에 insert/update 쿼리 실행

  > id 값이 있다면 update, 없다면 insert

 

- postsRepository.findAll() : posts 테이블에 있는 모든 데이터를 조회

 

- assertThat, isEqualTo : assertJ에서 지원하는 테스트 메서드

 

8. H2 콘솔 접속 방법

 8.1. localhost:port/h2-console 을 입력하여 H2 웹 콘솔 접속 및 JDBC URL 입력 후 Connect

H2 웹 콘솔 접속 정보 입력

 

 8.2. DB 접속 및 Entity 객체와 매핑된 테이블 확인

H2 웹 콘솔 접속 성공

  * Junit 테스트 시 11번째 줄의 deleteAll()을 주석처리하면 posts 테이블에 데이터가 들어가는 것을 확인할 수 있음

반응형
반응형

* 이동욱 저자의 스프링 부트와 AWS로 혼자 구현하는 웹서비스 교재 참고

 

1. 개요

 - 롬복 설치 및 롬복을 사용하여 Dto 객체를 생성한다.

 - 테스트용 controller를 생성한다.

 - Junit을 통해 테스트한다.

 

2. 롬복 설치 및 이클립스 연동

 2.1. 롬복 설치 (gradle)

org.projectlombok:lombok 추가

  - gradle에 롬복 의존성을 추가하여 라이브러리를 다운받는다.

 

 2.2. 롬복 실행

  - 라이브러리가 다운받아졌으면 롬복이 설치된 경로(gradle Home)로 이동한다.

  - 그 후 관리자 권한으로 cmd를 실행한 후 jar파일을 실행한다.

롬복 설치 경로

 2.3. STS 연동

  - 롬복 install 창이 뜨면 Specify location 을 선택하여 STS 실행파일을 선택 후 install /Update 를 클릭한다.

   * 여기서 access 에러가 뜬다면 cmd를 관리자 권한으로 실행하면 된다.

롬복 연동

 2.4. STS 연동 완료

  - 다음과 같은 창이 출력되면 설치 및 연동이 완료된 것이다.

  - STS 및 이클립스를 실행중이라면 재시작 시 적용된다.

연동 완료

3. HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.ssk.springboot.web;
 
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
import com.ssk.springboot.web.dto.HelloResponseDto;
 
@RestController
public class HelloController {
 
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
    
    @GetMapping("/hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name"String name, @RequestParam("amount"int amount){
        return new HelloResponseDto(name,amount);
    }
}
 
 
cs

 - @RestController : Json을 반환하는 컨트롤러로 만든다. @ResponseBody를 메서드마다 선언했던 것과 동일하다.

 - @GetMapping : Get 방식을 지원한다.

 - @RequestParam : request 파라미터를 가져온다.

 

4. HelloResponseDto.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ssk.springboot.web.dto;
 
import lombok.Getter;
import lombok.RequiredArgsConstructor;
 
@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
 
    private final String name;
    private final int amount;
}
 
cs

 - @Getter : 선언된 필드에 대해 Getter를 자동 생성한다.

 - @RequiredArgsConstructor : final로 선언된 필드가 포함된 생성자를 생성한다.

   > HelloResponseDto(String name, String amount) 라는 생성자가 내부적으로 생성된 것이다.

 

5. HelloControllerTest 클래스 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RunWith(SpringRunner.class//스프링 실행자를 JUnit 내에 저장된 실행자와 함께 실행
@WebMvcTest(controllers = HelloController.class// Mvc Test에 사용하는 어노테이션, 테스트 컨트롤러 리스트에 HelloController를 추가
public class HelloControllerTest {
    
    @Autowired
    private MockMvc mvc; //mvc 테스트용 객체
    
    @Test
    public void hello가_리턴된다() throws Exception{
        String hello = "hello";
        mvc.perform(get("/hello")) //hello로 get 통신
            .andExpect(status().isOk()) //200인지 검사
            .andExpect(content().string(hello)); //response 값이 hello 인지 검사
        
    }
    
    @Test
    public void helloDto가_리턴된다() throws Exception{
        String name = "hello";
        int amount = 1000;
        
        mvc.perform(get("/hello/dto")
                .param("name", name)
                .param("amount"String.valueOf(amount)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is(name)))
                .andExpect(jsonPath("$.amount", is(amount)));
    }
}
cs

 - @RunWith(SpringRunner.class) : 스프링 테스트에 필요한 스프링 실행자인 SpringRunner를 실행시킨다.

 - @WebMvcTest(controllers = HelloController.class) : 스프링 MVC 테스트에 사용하는 어노테이션이다. 테스트 controller로 HelloController를 지정했다.

 - mvc.perform(get("/hello")) : hello로 get요청을 보낸다. perform 메서드는 메서드 체이닝이 지원되며 andExpect 같은 검증 메서드와 함께 사용한다.

 - param("name",name) : 요청 파라미터 값을 추가한다. 단 String 형태만 허용되기 때문에 amount 값을 변환했다.

 - jsonPath : Json 응답값을 필드별로 검증할 수 있는 메서드이다. $를 기준으로 필드명을 명시한다. 

 

6. Junit 실행을 통한 테스트

Junit 실행

 - @Test 어노테이션이 붙은 메서드의 테스트가 정상적으로 실행됨을 확인할 수 있다.

 

 

반응형
반응형

1. 개요

 - gradle 프로젝트를 생성한다.

 - build.gradle을 작성하여 의존성을 주입한다.

 

2. Gradle 프로젝트 생성

 2.1. new / Spring Starter Project 선택

Spring Starter Project

 2.2. 프로젝트 초기 설정 입력

프로젝트 초기 설정 입력

 - Type을 Gradle, Java Version을 8로 선택 및 프로젝트 초기 설정 입력 후 Next를 선택한다.

 

2.3. 스프링 부트 프로젝트 설정

스프링 부트 프로젝트 설정

 - Web / Spring Web을 선택하고, Spring Boot Version은 임의로 2.4.4를 선택한다. 스프링 부트 버전은 build.gradle에서 수정이 가능하다.

 

 2.4. gradle 프로젝트 생성 완료

프로젝트 구조

 - 다음과 같은 구조로 프로젝트가 생성되었다.

 

3. build.gradle 작성

 

 3.1. build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
buildscript{
    ext{
        springBootVersion = '2.1.7.RELEASE'
    }
    repositories{
        mavenCentral()
        jcenter()
    }
    dependencies{
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
 
group = 'com.ssk'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
 
 
repositories{
    mavenCentral()
}
 
dependencies{
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
cs

 - build.gradle 파일은 말 그대로 gradle을 통해 빌드될 수 있도록 하는 설정파일이다.

 - 프로젝트 기본 설정, 의존성 주입, 플러그인, 레포지토리 설정 등의 정보가 들어있다.

 - ext는 전역변수를 설정하겠다는 의미이며, 10번째 라인을 보면 스프링 부트 그레들 플로그인의 2.1.7.RELEASE를 의존성으로 받겠다는 의미이다.

 - repositories는 저장소를 의미하며, mavenCentral은 메이븐 중앙 레포지토리, jcenter는 mavenCentral 저장소의 문제점을 개선한 레포지토리이다. 일반적으로 위처럼 이 두가지 저장소 정보를 넣어준다.

 - dependencies는 의존성을 추가하는 부분이다.

 - 13 ~ 16 라인의 플러그인들은 자바와 스프링을 사용하기 위한 필수 플러그인들이므로 필히 추가해야 한다.

 

 3.2. build.gradle 실행

build.gradle 실행

 - build.gradle 우클릭 / Gradle / Refresh Gradle Project를 선택하여 build.gradle을 실행한다.

 - 정상적으로 실행되었다면 프로젝트 라이브러리에 의존성이 추가된 것을 확인할 수 있다.

 

4. 에러 리포트

 4.1. could not run phased build action using connection to gradle distribution 에러

 

 첫번째 솔루션. project / properties / Gradle / 설정 변경

 - Gradle user home 및 java home 경로를 명시적으로 기입해준다.

 

 두번째 솔루션. build.gradle 소스 문제

  - 위 문제로도 해결이 되지 않아 코드 확인 중 plugin 관련 코드를 sourceCompatibility 아래에 적어놨던 것을 뒤늦게 확인했다. plugin 코드들을 sourceCompatibility 하위에 적어뒀다면 위 예제처럼 위로 올리면 해결된다.

반응형

+ Recent posts