컴공선배_22Days 챌린지 15일차

Title

💡 스프링부트 프로젝트 구조 살펴보기 / 의존성 / 내장 웹서버 / JAR / [Application.properties](http://Application.properties) 2

✏️1. 스프링 부트 프로젝트 구조


💡 src/main/java = 클래스 파일들을 모아 놓은 곳
  • src/main/resources 경로 = xml, properties를 저장하는 곳으로
  1. static = css,js,hteml의 파일을 관리하는 곳
  2. templates = 스프링 부트에서 이용하는 타임리프(thymeleaf) 형식 템플릿 파일을 저장하는 곳
  3. **application.properties = 스프링 부트 전체 프로젝트의 설정을 저장하는 곳**
  • src/test/java 경로 = JUnit 등 테스트 케이스의 자바 클래스 파일들을 저장하는 곳

자료 출처: https://medium.com/@dlaudtjr07/spring-boot-unit-2-프로젝트-구조-1893562186e0

✏️2. 스프링 부트의 의존성


<!-- Inherit defaults from Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>

<!-- Add typical dependencies for a web application -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Spring Boot의 의존성 관리 기능 , 스프링 부트는 자동 환경 설정을 제공한다.

2. 의존성 관리 기능 활용

(1) 버전 관리 해주는 의존성 추가

Spring Data JPA라는 의존성을 추가

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
  • spring-boot-starter 를 추가했기 때문에 자동으로 의존성이 추가된다

(2) 버전 관리 안해주는 외부 의존성 추가

https://mvnrepository.com/

Maven dependency 검색 사이트

<dependency><groupId>org.modelmapper</groupId><artifactId>modelmapper</artifactId><version>2.1.0</version></dependency>
  • 외부에서 가져오는 의존성은 버전을 명시해주는 것이 좋다.

(3) 기존 의존성 버전 변경하기

spring-boot-dependencies-2.0.3.RELEASE.pom에 버전들이 정리되어있다

...

<snakeyaml.version>1.19</snakeyaml.version><solr.version>6.6.4</solr.version><spring.version>5.0.7.RELEASE</spring.version><spring-amqp.version>2.0.4.RELEASE</spring-amqp.version><spring-batch.version>4.0.1.RELEASE</spring-batch.version><spring-cloud-connectors.version>2.0.2.RELEASE</spring-cloud-connectors.version><spring-data-releasetrain.version>Kay-SR8</spring-data-releasetrain.version><spring-hateoas.version>0.24.0.RELEASE</spring-hateoas.version>

...

Spring Boot에서 관리해주는 의존성 버전을 변경하고 싶을 때는 properties에 버전을 추가해 Overriding 해주면 된다.

<properties><spring.version>5.0.6.RELEASE</spring.version></properties>
  • 5.0.7 RELESASE 에서 5.0.6 RELEASE로 변경된 것을 확인할 수 있다.

자료 출처: https://velog.io/@dsunni/Spring-Boot-스프링부트-원리-의존성-관리-자동-설정

✏️3. 스프링 부트의 내장 웹서버


스프링 부트 ≠ 서버

Spring 프레임워크를 쉽게 사용할 수 있게해주는 Tool일 뿐, 스프링 부트 자체는 웹 서버가 아니다.

내장 서블릿 컨테이너 개요

spring-boot-starter-web을 사용한 프로젝트엔 톰캣(Tmcat)이 기본적으로 내장되어 있다. 톰캣은 데이터를 동적으로 처리하기 위한 서블릿 컨테이너로, JSP, 서블릿 처리, HTTP 요청과 응답 등을 처리한다.

자동 설정이 없다고 가정하고 Servlet을 만들어서 톰캣에 등록해보자.

@SpringBootApplicationpublicclassApplication {

publicstaticvoidmain(String[] args) {
        Tomcat tomcat =new Tomcat();// 톰캣 객체 생성
        tomcat.setPort(8080);// 포트 설정
        Context context = tomcat.addContext("/", "/");// 톰캣에 컨텍스트 추가// 서블릿 객체 생성
        HttpServlet servlet =new HttpServlet() {
@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
                PrintWriter writer = resp.getWriter();
                writer.println("<h1>Hello world!</h1>");
            }
        };

final String servletName = "myServlet";
// 톰캣에 서블릿 추가
        tomcat.addServlet("/", servletName, servlet);
// 컨텍스트에 서블릿 매핑
        context.addServletMappingDecoded("/home", servletName);

try {
// 톰캣 실행 및 대기
            tomcat.start();
            tomcat.getServer().await();
        }catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}

웹 브라우저를 켜서 [http://localhost:8080/home](http://localhost:8080/home)으로 접속해보면 doGet()에서 작성한 내용이 HTML 문서로 반환된다.

스프링에서는 이렇게 Tomcat에 Servlet을 등록하고 실행하는 일련의 작업이 자동 설정으로 정의되어 있다. 자동 설정 덕분에 이 모든 작업을 상세하고 유연하게 설정 후 실행해준다.

  • ServletWebServerFactoryAutoConfiguration : 서블릿 웹 서버 생성하는 설정 파일
    • TomcatServletWebServerFactory를 들어가보면 톰캣을 생성하고 서블릿, 디스패처 설정을 하는 코드가 있다.
      public WebServer getWebServer(ServletContextInitializer... initializers) {
                Tomcat tomcat = new Tomcat();
                File baseDir =this.baseDirectory != null ?this.baseDirectory :this.createTempDir("tomcat");
                tomcat.setBaseDir(baseDir.getAbsolutePath());
                Connector connector = new Connector(this.protocol);
                tomcat.getService().addConnector(connector);
      this.customizeConnector(connector);
                tomcat.setConnector(connector);
                tomcat.getHost().setAutoDeploy(false);
      this.configureEngine(tomcat.getEngine());
      // ...
        }
      ServletWebServerFactoryAutoConfiguration에서는 TomcatServletWebServerFactory를 사용하는 TomcatServletWebServerFactoryCustomizer를 반환한다.
        @Configuration
        @AutoConfigureOrder(-2147483648)
        @ConditionalOnClass({ServletRequest.class})
        @ConditionalOnWebApplication(
            type = Type.SERVLET
        )
        @EnableConfigurationProperties({ServerProperties.class})
        @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
        public class ServletWebServerFactoryAutoConfiguration {
      // ...publicTomcatServletWebServerFactoryCustomizertomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
      returnnewTomcatServletWebServerFactoryCustomizer(serverProperties);
              }
      // ...
        }
  • DispatcherServletAutoConfiguration : 서블릿을 만들고 등록해주는 설정 파일

자료 출처: https://zion830.tistory.com/115

✏️4. 스프링 부트의 JAR


JAR? WAR?

기본적으로 JAR, WAR 모두 Java의 jar 옵션 (java -jar)을 이용해 생성된 압축(아카이브) 파일로, 애플리케이션을 쉽게 배포하고 동작시킬 수 있도록 관련 파일(리소스, 속성 파일 등)을 패키징 한 것이다.

자세한 정의는 아래와 같다.

JAR (Java Archive)

  • JAVA 어플리케이션이 동작할 수 있도록 자바 프로젝트를 압축한 파일
  • Class (JAVA리소스, 속성 파일), 라이브러리 파일을 포함함
  • JRE(JAVA Runtime Environment)만 있어도 실행 가능함 (java -jar 프로젝트네임.jar)

2. JAR File Structure

스프링 부트 공식문서에서 소개하는 JAR 파일은 아래 이미지처럼 BOOT-INF, META-INF, org 세 폴더로 이루어져 있다.

2.1. BOOT-INF

: 개발자가 직접 작성한 클래스 파일들(classes)과, 의존성 주입을 통한 jar 파일(lib)들로 구성되어 있다.

  • classpath.idx

그림에는 없지만 classpath.idx라는 파일도 포함되어 있는데, 이는 classpath에 추가될 jar 파일들의 목록(lib 폴더 안에 있는 jar 파일)을 정의한 것이다.

이는 "일반적인 JAR 파일은 중첩된 JAR 구조를 지원하지 않는다"는 단점을 보완하기 위해 스프링 부트가 고안해 낸 방법으로, jar 파일 리스트를 확인하여 중첩된 JAR 구조를 지원할 수 있도록 한다.

- "ojdbc8.jar"
- "sitemesh-3.0.1.jar"
- "querydsl-apt-4.1.4.jar"
- "spring-boot-starter-web-2.3.4.RELEASE.jar"
- "spring-boot-starter-data-jpa-2.3.4.RELEASE.jar"
- "querydsl-jpa-4.3.1.jar"
- "spring-boot-starter-security-2.3.4.RELEASE.jar"
…

2.2. META-INF

: 프로젝트 매니페스트 파일(MANIFEST.MF)을 포함하는 폴더이다. 매니페스트 파일은 파일 그룹을 위한 메타데이터(이름, 버전 번호, 라이선스, 프로그램의 구성 등)를 포함하는 파일이다.

  • MANIFEST.MF
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Start-Class: com.example.demo.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.3.4.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher

일반적인 JAR File의 MANIFEST에서 Main-Class는 Main메서드가 존재하는 클래스로 설정되지만, 스프링 부트의 Main-Class에서는 JarLauncher라는 클래스로 설정되어있다. (cf. WAR File에서는 WarLauncher)

  • JarLauncher를 통한 Jar의 실행방식은 아래 순서로 이루어진다.
  1. org.springframework.boot.loader.jar.JarFile: 내장 jar 인식
  2. org.springframework.boot.loader.Launcher 실행
  3. Start-Class에 선언된 클래스의 Main메서드 실행 (com.example.demo.DemoApplication)

2.3. org

: org 폴더 안에는 위에서 설명한 Springboot loader classes 모듈이 저장되어 있다.

3. WAR File Structure

WAR 파일은 아래 이미지처럼 WEB-INF, META-INF, org 세 폴더로 이루어져 있다. JAR File과의 차이점은 BOOT-INF 대신 WEB-INF가 있다는 것인데, 웹 애플리케이션의 사전 정의된 구조를 따르기 위한 것으로 보인다.

3.1. WEB-INF

: 개발자가 직접 입력한 class와 jar 파일, JSP 일 경우 view 파일들까지 포함되어 있는 디렉터리 이다. 아래 이미지는 Tomcat 사이트에서 설명하는 기본 디렉토리 구조로, WAR File Structure의 구조와 유사하단 것을 알 수 있다. 이처럼 웹 애플리케이션의 사전 정의된 구조를 따르고 있기 때문에, 외장 WAS 나 JSP를 사용할 일이 있다면 WAR를 이용하여 배포해야 한다.

https:// tomcat.apache.org/tomcat-9.0-doc/appdev/deployment.html

3.2. META-INF

: JAR File과 동일하게 MANIFEST.MF 파일을 확인할 수 있다. 간혹 WAR파일은 단독으로 실행이 불가능하다는 이야기를 듣곤 했는데, Spring boot의 WAR는 단독으로 실행이 가능하다(bootWar로 빌드하였을 경우). 그 이유는 Main-Class에 Spring loader로 설정되어 있기 때문이다.

동작 원리는 JarLauncher와 유사하므로, MANIFEST.MF 파일 구조만 기재하고 넘어간다.

Manifest-Version: 1.0
Start-Class: com.example.demo.DemoApplication
Spring-Boot-Classes: WEB-INF/classes/
Spring-Boot-Lib: WEB-INF/lib/
Spring-Boot-Version: 2.3.4.RELEASE
Main-Class: org.springframework.boot.loader.WarLauncher

자료출처:https://hye0-log.tistory.com/27

✏️5. 스프링 부트의 application.properties


외부 설정 파일 이란?

애플리케이션에서 사용하는 여러가지 설정 값들을 애플리케이션의 밖이나 안에 정의할 수 있는 기능을 말한다.

application.properties

이 파일은 스프링부트가 애플리케이션을 구동할 때 자동으로 로딩하는 파일이다.key - value 형식으로 값을 정의하면 애플리케이션에서 참조하여 사용할 수 있다.

값을 참조할 때는 여러가지 방법이 있다. @Value 어노테이션으로 값을 받아올 수도 있다.

프로퍼티 random 값 사용

application.properties에 ${random.무엇}으로 랜덤 값을 줄 수도 있다.

프로퍼티 place holder

application.properties에 정의해준 값을 변수를 사용하듯이 그대로 사용할 수도 있다.

프로퍼티 우선순위

프로퍼티를 설정할 수 있는 방법은 매우 다양하다. 따라서 각 방법마다 우선순위가 있다. 여러 방법으로 같은 프로퍼티를 정의하고 있으면 우선순위가 제일 높은 방법으로 정의한 프로퍼티 값이 오버라이딩된다.

우선순위는 아래와 같다.1. 유저 홈 디렉토리에 있는 spring-boot-dev-tools.properties

  1. 테스트에 있는 @TestPropertySource
  2. @SpringBootTest 애노테이션의 properties 애트리뷰트
  3. 커맨드 라인 아규먼트
  4. SPRING_APPLICATION_JSON (환경 변수 또는 시스템 프로티) 에 들어있는 프로퍼티
  5. ServletConfig 파라미터
  6. ServletContext 파라미터
  7. java:comp/env JNDI 애트리뷰트
  8. System.getProperties() 자바 시스템 프로퍼티
  9. OS 환경 변수
  10. RandomValuePropertySource
  11. JAR 밖에 있는 특정 프로파일용 application properties
  12. JAR 안에 있는 특정 프로파일용 application properties
  13. JAR 밖에 있는 application properties
  14. JAR 안에 있는 application properties
  15. @PropertySource
  16. 기본 프로퍼티 (SpringApplication.setDefaultProperties)

resources - application.properties는 15번 순위이다.

우선 순위가 높은 test에서 프로퍼티를 설정해보겠다.

test 디렉토리 밑에 java에 클래스를 만들고 @SpringBootTest, @RunWith(SpringRunner.class) 어노테이션을 붙여준다.

@Test 어노테이션을 붙여 메서드를 하나 만들어준다.

모든 프로퍼티들을 기본적으로 Environment를 통해서 확인할 수 있다. 따라서 Environment를 주입받아 getProperty로 프로퍼티를 받아온다.

그리고 test 디렉토리에 resources 디렉토리를 만들고 project structure에서 Modules 부분에서 만들어준 resources를 Test Resources로 바꿔준다.

그 후, 하위에 application.properties를 만들어준다. 그리고, 프로퍼티의 키는 그대로두고, 값을 바꿔준다.

그 후 테스트 파일을 돌려보면 우선순위가 높은 테스트파일의 프로퍼티가 출력된다.

테스트코드를 실행하면, 먼저 테스트코드가 빌드가 되고, 빌드를 할 때, src를 전부 빌드하여 classpath에 놓는다. 그 다음에 테스트코드를 빌드하여 classpath에 놓는다. 그 때, application.properties가 바뀌는 원리이다.

그렇기 때문에 만약 main의 application.properties에 정의되어 있는 값이 있고, test의 application.properties에는 그 값이 없으면 테스트가 깨진다. 실행 시 application.properties가 테스트 것으로 덮어쓰여지기 때문이다. 그래서 버그가 안나게 하기 위해서는 test의 것에도 똑같은 key값을 넣어줘야한다.

@TestPropertySource라는 어노테이션을 사용해서도 프로퍼티를 정해줄 수 있다. 우선순위가 2순위인 방법이다.

만약 설정해주고 싶은 프로퍼티 값이 여러개일 경우 .properties 파일을 만들어 적용해 줄 수도 있다.

classpath 기준으로 만들어준 파일의 위치를 적어준다.

application.properties의 우선순위

application.properties를 같은 위치가 아니라 다른 위치에 둘 경우는 컴파일해도 덮어쓰지 않게된다.

이 때 우선순위는 아래와 같다.1. file:./config/ :프로젝트 디렉토리 바로 아래에 config라는 디렉토리를 만들고 그 안에 application.properties 만들어주는 방법

  1. file:./ :프로젝트 루트 바로 아래에 application.properties 만들어주는 방법
  2. classpath:/config/ :classpath 아래에 config라는 디렉토리를 만들고 그 안에 application.properties 만들어주는 방법
  3. classpath:/ :classpath 아래에 application.properties 만들어주는 방법

자료 출처: https://velog.io/@max9106/Spring-Boot-외부설정-uik69crax3

좋은 웹페이지 즐겨찾기