2018년 초가을 대전에서 모 프로젝트를 진행했을 때, 고객님께서 무조건 톰캣에 war로 배포를 해달라는 요청이 있었습니다.

프로젝트 막판까지 개발은 SpringBoot로 개발하고 Jar 배포로 다 합의된 사항이었지만.. 마지막에 고객님의 강력한 요구로 war로 묶어서 톰캣에 올렸던 추억을 생각하면서 어떤 부분을 고쳐야 war로 묶을 수 있는지 보겠습니다.

pom.xml 에서

<packaging\>jar</packaging\>

부분을

<packaging\>war</packaging\>

로 변경을 해줍니다.

war로 패키징 한 후에 톰캣에 올라갈 수 있게 spring-boot-starter-tomcat 을 추가해줍니다.

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

그러고 나서 Build 부분에 finalName 추가해 줍니다.

<build>
    <finalName>d-sample</finalName>
</build>

이미지로 정리해 보겠습니다.

pom.xml

Run As -> Maven build

Goals : package 을 입력 후  Run

톰캣 -> webapp -> war 파일 말아서 올리고 실행한 결과

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

[SpringBoot] 파일 다운로드  (0) 2020.07.02
[JPA] JPA Auditing  (0) 2020.02.16
[JUnit] 테스트 기본 - 2  (0) 2020.02.13
[JUnit] 테스트 기본 - 1  (0) 2020.02.13
[JPA] 트랜잭션과 락  (0) 2020.02.12

VideoRestController.java

@GetMapping(value = "/{videoId}/download", produces = MediaType.APPLICATION_JSON_VALUE)
public StreamingResponseBody findVideoFileDownload(@PathVariable(name="videoId") String videoId,HttpServletResponse response) throws FileNotFoundException {

// TODO Auto-generated method stub
return this.videoServiceImpl.findVideoFileDownload(videoId, response);

}

VideoService.java


public interface VideoService {

    public StreamingResponseBody findVideoFileDownload(String videoId, HttpServletResponse response) throws FileNotFoundException;


}

VideoLogic.java

public class VideoLogic implements VideoService {

    private VideoRepositoryStore videoRepository;

    public VideoLogic(VideoRepositoryStore videoRepository) {
        this.videoRepository = videoRepository;
    }


    @SuppressWarnings("resource")
    @Override
    public StreamingResponseBody findVideoFileDownload(String videoId, HttpServletResponse response) throws FileNotFoundException {

        /*
         * 다운 받을 영상 경로 예 : /Users/kabby/Desktop/video/tea.mp4
         *
         * 확장자 : mp4
         * 
         */

        Video video=this.videoRepository.retrieveVideoById(videoId);

        response.setContentType("video/mp4");
        response.setHeader("Content-Disposition", "attachment;filename=" + video.getVideoName() + "." + video.getVideoExtention());

        File targetFile = new File(video.getVideoPath());
        InputStream targetStream =  new DataInputStream(new FileInputStream(targetFile));

        return outputStream -> {
            int nRead;
            byte[] data = new byte[4069];
            while ((nRead = targetStream.read(data, 0, data.length)) != -1) {
                outputStream.write(data, 0, nRead);
            }

        };
    }

}

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

[SpringBoot] war 배포  (0) 2020.07.02
[JPA] JPA Auditing  (0) 2020.02.16
[JUnit] 테스트 기본 - 2  (0) 2020.02.13
[JUnit] 테스트 기본 - 1  (0) 2020.02.13
[JPA] 트랜잭션과 락  (0) 2020.02.12

엔티티에는 보통 해당 데이터의 생성,수정 시간이 포함한다.
예를 들어 새로운 회원이 언제 가입했는지, 그리고 로그인을 마지막으로 언제 했는지를 알기 위해서도 필요한 부분이다.
그럴 때 이제 엔티티마다 날짜 데이터를 등록/수정 하는 코드를 추가하게 되는데, 이럴 때마다 코드는 반복되고, 결국엔 지저분 해지는 결과를 초래한다. 이러한 문제를 해결하는 방법은 뭐가 있을까?
바로 "JPA Auditing" 이다.

1. BaseTimeEntity 만들고, @EntityListeners 을 통해 BaseTimeEntity 클래스가 Auditing 기능을 쓸 수 있도록 만든다.

package com.incheol.app.domain.post;

import java.time.LocalDateTime;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Getter;

@Getter
@MappedSuperclass // 엔티티들이 BaseTimeEntity을 상속할 경우 BaseTimeEntity에 존재하는 필드들을컬럼으로 인식하도록 하는 어노테이션.
@EntityListeners(AuditingEntityListener.class) // BaseTimeEntity 추상클래서에 Auditing 기능을 포함시키는 어노테이
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

}

2. SpringBootAppApplication 클래스 위에 JPA Auditing 을 활성화 할 수 있도록 어노테이션을 추가한다.

@EnableJpaAuditing //JPA Auditing 어노테이션을 활성화 시키도록 하는 어노테이션.
@SpringBootApplication
public class SpringBootAppApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAppApplication.class, args);
    }

}

3. Jpa Auditing을 쓸 Entity에 BaseTimeEntity을 상속시켜준다.

 BaseTimeEntity을 상속 시킴으로써 Post 엔티티에는 아래와 같이 createdDate, modifiedDate 를 쓸수 있다고 보면 된다. 

 @CreatedDate
 private LocalDateTime createdDate;

 @LastModifiedDate
 private LocalDateTime modifiedDate;
@Getter
@NoArgsConstructor // 기본 생성자 자동 추가 된다.
@Entity // 테이블과 매핑 될 클래스인 것을 나타낸다.
public class Post extends BaseTimeEntity {

    @Id //PK를 나타낸다.
    @GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increament 을 나타내기위한 전략
    private Long id;

    @Column(nullable = false , length = 500) // 컬럼에 대한 옵션 즉, not null과 컬럼의 길이를 정의할 수 있다. 
    private String title;

    @Column(columnDefinition= "TEXT", nullable = false) // 컬럼의 길이를 500자 이상으로 나타내고 싶을 때 columnDefinition = "TEXT" 의 옵션을 적어준다.
    private String content;

    private String author; // 변수 위에 @Column을 붙이지 않아도, 클래스 위에 선언 되어있는 @Entity 어노테이션 때문에 변수 명을 기준으로 테이블의 컬럼이 디폴드로 생겨난다.  

     /*
     * BaseTimeEntity을 상속 시킴으로써 Post 엔티티에는 아래와 같이 createdDate, modifiedDate 를 쓸수 있다고 보면 된다. 
     * 
     *     @CreatedDate
     *  private LocalDateTime createdDate;
     * 
     *  @LastModifiedDate
     *  private LocalDateTime modifiedDate;
     */

    @Builder // 해당 클래스의 빌더 패턴 클래스를 생성한다.
    public Post(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content ) {
        this.content=content;
        this.title=title;
    }

}

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

[SpringBoot] war 배포  (0) 2020.07.02
[SpringBoot] 파일 다운로드  (0) 2020.07.02
[JUnit] 테스트 기본 - 2  (0) 2020.02.13
[JUnit] 테스트 기본 - 1  (0) 2020.02.13
[JPA] 트랜잭션과 락  (0) 2020.02.12

오늘은 url 호출에서 parameter가 존재할 때 테스트를 어떻게 하는지 알아본다.

  1. 아래와 같이 name 과 age 파라미터를 통해 url을 Get으로 호출 하려고 할 때 코드와 테스트 코드를 어떻게 작성하는지 알아본다.

    http://localhost:8080/hello/dto?name=incheol&age=28 
  2. HelloResponseDto.java 을 생성한다.

package com.incheol.app.web.dto;
import lombok.Getter;  
import lombok.RequiredArgsConstructor;

@Getter  
@RequiredArgsConstructor  
public class HelloResponseDto {

private final String name;
private final int age;

}
  1. HelloResponseDtoTest.java 을 생성한다.

  테스트를 할땐 Given, When, Then 세 가지를 기억하면 된다.  
 Given : 시나리오 진행에 필요한 값을 설정한다.  
  When : 시나리오를 진행하는데 필요한 조건을 명시한다.  
  Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
package com.incheol.app.web.dto;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;

public class HelloResponseDtoTest {

    @Test
    public void lombokTest() {

        //Given : 시나리오 진행에 필요한 값을 설정한다.
        String name = "incheol";
        int age = 28;

        //When : 시나리오를 진행하는데 필요한 조건을 명시한다.
        HelloResponseDto dto = new HelloResponseDto(name, age);

        //Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
        assertThat(dto.getName()).isEqualTo(name);
        assertThat(dto.getAge()).isEqualTo(age);

    }
}
  1. Junit 실행시켜서 테스트가 제대로 수행 되었는지 확인한다.

  1. HelloController.java 에 Get /hello/dto API을 추가한다.

package com.incheol.app.web;

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

import com.incheol.app.web.dto.HelloResponseDto;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping("/hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("age") int age) {
        return new HelloResponseDto(name, age);
    }

}
  1. 4번에서 추가한 기능을 테스트 코드로 작성해서 테스트를 해본다.

package com.incheol.app.web;
import static org.hamcrest.CoreMatchers.is;  
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;  
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;  
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;  
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;  
import org.junit.runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;  
import org.springframework.test.context.junit4.SpringRunner;  
import org.springframework.test.web.servlet.MockMvc;

@RunWith(SpringRunner.class)  
@WebMvcTest // Web(Spring MVC)에 집중할 수 있는 어노테이션  
public class HelloControllerTest {


  @Autowired
  private MockMvc mvc; //웹 API을 테스트 할때 사용하는 가짜 데이터 , GET, POST 등에 대한 테스트를 할 수 있음.


  @Test
  public void returnHello() throws Exception {
      String hello = "hello";

      //테스트를 하는 행위(perform) => 하나씩 기대 값을 넣고 테스트를 한다. 
      mvc.perform(get("/hello"))
      .andExpect(status().isOk())
      .andExpect(content().string(hello));
  }

  @Test
  public void returnHelloResponseDto() throws Exception {

      //Given : 시나리오 진행에 필요한 값을 설정한다.
      String name = "kabby";
      int age = 28;

      //When : 시나리오를 진행하는데 필요한 조건을 명시한다.
      //Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
      mvc.perform(get("/hello/dto")
              .param("name", name) // name => kabby
              .param("mount", String.valueOf(age))) // age => 28
              .andExpect(status().isOk()) // 파라미터 2개의 기대 값에 대하여 상태 값 200을 받는지 비교한다.
              .andExpect(jsonPath("$.name", is(name))) // {name: "kabby"} => jsonPath을 통해 값을 비교를 한다.
              .andExpect(jsonPath("$.mount", is(age))); // {age: 28}  => jsonPath을 통해 값을 비교를 한다.


      }


   }
  1. Junit 실행시켜서 테스트가 제대로 수행 되었는지 확인한다.

  1. 실제로 화면에서 {"name":"kabby","age":28} 이 나왔는지 확인한다.

오늘은 웹 API을 테스트 할 때 Get 기준 파라미터가 존재 할때 어떻게 테스트를 하는지 알아봤다.

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

[SpringBoot] 파일 다운로드  (0) 2020.07.02
[JPA] JPA Auditing  (0) 2020.02.16
[JUnit] 테스트 기본 - 1  (0) 2020.02.13
[JPA] 트랜잭션과 락  (0) 2020.02.12
[STS4] Mac 단축키 변경 (자동완성)  (0) 2020.02.11

+ Recent posts