본문 바로가기

Spring

[김영한의 스프링 입문] 1. 프로젝트 환경 설정

 프로젝트 생성하기 

사전 준비

  • Java 11 설치
  • IDE intelliJ (무료버전) 또는 Eclipse 

스프링 부트로 프로젝트 생성하기

스프링 부트 스타터 사이트로 이동해서 원하는 설정으로 스프링 프로젝트를 생성할 수가 있다.

이 때, 프로젝트에서 사용할 라이브러리를 두 개 추가한다.

  • spring web
  • thymeleaf

프로젝트 구조

이를 통해서 생성된 파일 밑 build.gradle을 intelliJ에서 open 해주면 IntelliJ IDE에서 사용할 수 있도록 프로젝트 파일이 임포트된다.

이 IDE에서 보여지는 해당 프로젝트의 구조는 다음과 같다.

  • .gradle
  • .idea : 인텔리제이가 사용하는 설정 파일
  • gradle : gradle 관련 설정 파일이 들어가는 폴더
  • src
    • main
      • java : 자바 파일들이 들어가는 폴더
      • resources: 자바 파일 이외의 모든 파일이 들어가는 폴더
        • static :정적 파일이 들어가는 폴더
        • templates : 템플릿 엔진이 사용할 파일이 들어가는 폴더
    • test : 테스트 코드와 관련된 파일들이 들어가는 폴더
      • java
  • .gitignore : 깃허브에 올라가면 안되는 요소(ex - build된 결과물들)을 지정한 파일
  • build.gradle : 버전 설정과 라이브러리를 다운받는데 사용되는 폴더
  • gradlew
  • gradlew.bat
  • HELP.md
  • settings.gradle

build.gradle에서는 다음과 같은 설정들이 존재한다.

  • repositories -  라이브러리를 다운 받는 경로를 지정
  • dependencies - 프로젝트에서 다운 받을 라이브러리들을 지정
plugins {
	id 'org.springframework.boot' version '2.4.1'
	id 'io.spring.dependency-management' version '1.0.10.RELEASE'
	id 'java'
}

group = 'com.heejin'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

 

서버 실행

또한 src/main/java com.heejin(패키지 경로).hellospring 밑에 자동생성된 HelloSpringApplication.java를 확인할 수가 있는데, 다음과 같이 작성되어 있다.

package com.heejin.hellospring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloSpringApplication {

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

}

인텔리제이에서 바로 여기 정의된 메인 메서드를 실행하면 콘솔에서 다음과 같이 정상적으로 실행되었음을 알린다.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.1)

2021-01-04 08:06:15.896  INFO 2339 --- [           main] c.h.hellospring.HelloSpringApplication   : Starting HelloSpringApplication using Java 11.0.7 on choehuijin-ui-MacBookPro.local with PID 2339 (/Users/heejin/Desktop/hello-spring/out/production/classes started by heejin in /Users/heejin/Desktop/hello-spring)
2021-01-04 08:06:15.898  INFO 2339 --- [           main] c.h.hellospring.HelloSpringApplication   : No active profile set, falling back to default profiles: default
2021-01-04 08:06:16.913  INFO 2339 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-01-04 08:06:16.922  INFO 2339 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-01-04 08:06:16.922  INFO 2339 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-01-04 08:06:16.982  INFO 2339 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-01-04 08:06:16.982  INFO 2339 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1038 ms
2021-01-04 08:06:17.153  INFO 2339 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-01-04 08:06:17.220  INFO 2339 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2021-01-04 08:06:17.360  INFO 2339 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-01-04 08:06:17.374  INFO 2339 --- [           main] c.h.hellospring.HelloSpringApplication   : Started HelloSpringApplication in 1.86 seconds (JVM running for 2.642)
2021-01-04 08:06:20.558  INFO 2339 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-01-04 08:06:20.558  INFO 2339 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-01-04 08:06:20.559  INFO 2339 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2021-01-04 08:18:56.764  INFO 2339 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

그런데 이 중에서 embedded.tomcat.TomcatWebServer도 실행되었음을 알 수가 있는데 HelloSpringApplication의 main 메서드를 실행하면 스프링 애플리케이션 클래스에 대하여 .run을 실행함으로써 이 안에 내장된 톰캣 서버를 띄우면서 스프링부트가 같이 올라오는 원리로 동작한다.

 

브라우저를 켜서 주소창에 localhost:8080을 입력하여 엔터를 쳤을때 다음과 같은 에러가 뜬다면, 서버가 잘 작동하고 있는 것이다.

빌드나 런을 할때 gradle을 통해 실행되는 것이 아니라, intelliJ에서 곧바로 실행시키고자 한다면, preferences -> Gradle -> Build and Run 카테고리에서 Build and run using 과 Run tests using 설정을 gradle -> intelliJ로 바꿔준다.

 라이브러리 살펴보기 

라이브러리의 의존관계

build.gradle의 dependencies를 살펴보면 세개밖에 없는데 external libraries를 살펴보면 이에 비해 훨씬 더 많은 라이브러리가 존재한다. gradle이나 maven 빌드 툴들은 의존관계를 관리해준다. 스프링 부트 스타트업을 가져오면 이 라이브러리가 필요한 다른 라이브러리(tomcat, mvc)등을 함께 가져오는 것이다. 이런 방식으로 가장 밑에 존재하는 스프링 코어까지 모든 의존관계를 끝까지 가져오게 된다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

이 의존관계를 gradle 창을 열어서 확인할수가 있다. compilepath 폴더를 열면 내가 직접 땡겨온 라이브러리 두개를 볼수가 있고, 각각의 폴더를 열면 이 라이브러리에 필요헀던 다른 라이브러리까지 모두 보여주고있는 것이다. 이런 방식으로 계속 타고 들어가면서 한번에 땡겨오는 것이다. (*)은 이미 다른 라이브러리에서 필요하여 다운을 받았기 때문에 중복을 제거한 것을 의미한다. 더블클릭하면 그 경로를 찾아갈수가 있다. 

 

스프링 부트 라이브러리에는 다음과 같은 구조로 이뤄져있다.

  • spring-boot-starter-web
    • spring-boot-starter-tomcat : 톰캣(웹서버)
    • spring-webmvc : 스프링 웹 mvc
  • spring-boot-starter-thymeleaf : 타임리프 템플릿 엔진(View)
  • spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅
    • spring-boot
      • spring-core
    • spring-boot-starter-logging
      • logback, slf4j

테스트 라이브러리는 다음과 같은 구조로 이뤄져있다.

  • junit : 테스트 프레임워크
  • mockito : 목 라이브러리
  • assertj : 테스트 코드를 좀더 편하게 작성하도록 도와주는 라이브러리
  • spring-test : 스프링 통합 테스트 지원

spring-boot-starter-tomcat

Spring-boot-starter-web 라이브러리 안에는 톰캣 라이브러리가 존재하는데, 이 라이브러리는 별도의 톰캣 웹서버를 설치, 배포, 실행하는 복잡한 과정을 줄여준다. 따라서 자바 코드를 실행하기만 해도 톰캣 서버가 실행된다. 이것을 embedded라고 한다.

spring-boot-starter-core

spring-boot-starter 에는 스프링 부트와 관련된 라이브러리가 전부들어가있으며 스프링 코어도 가져오고 있다. 가장 밑바닥에 있는 스프링 코어까지 모두 가져오고있음을 알 수 있다. 이것 또한 gradle 창에서 검색하면 의존관계를 확인할 수가 있다.

spring-boot-starter-logging

서버를 실행할 때마다 실행되는 모든 결과물들을 log로 남겨야 심각한 에러만 따로 분리할 수가 있으므로 log 라이브러리는 중요하며, 실무에서도 많이 사용된다. 그리고 해당 라이브러리 안에는 logback과 slf4j이 있는데, slf4j은 인터페이스고 logback라는 이 인터페이스의 구현체이다. 대체로 이 조합이 많이 사용되므로 springboot에서도 이것을 표준으로 정하여 미리 의존 라이브러리로 다운받아진 것이다.

spring=boot-starter-test

테스트와 관련된 라이브러리도 존재하는데, 그 중에서 junit이라는 라이브러리가 실무에서 많이 된다. 실무에서는 버전 4가 오래 유지되다가 최근에 5로 넘어가는 추세이다. assertj는 테스트를 편리하게 해주는 라이브러리이며, spring-test라는 라이브러리는 spring과 통합해서 테스트할수있도록 도와주는 라이브러리이다.


 View 환경 설정 

정적 페이지 띄우기

프로젝트를 실행을 해도 loalhost:8080을 입력하여 엔터를 치면 계속 에러 페이지가 뜨고있었는데 이제 간단한 welcome 화면을 띄우려고 한다. 정적 파일을 넣는 resources/static에 Index.html 파일을 만들어 넣으면 홈 화면에서 이 웹문서를 띄운다.

이 동작과 관련한 문서는 spring.io에서 내가 사용하고있는  spring boot 2.4.1 버전의 document에서 spring boot features 파트의 7.1.6 Welcome Page에서 다음과 같이 확인할 수 있다.

Spring Boot supports both static and templated welcome pages. 
It first looks for an index.html file in the configured static content locations. 
If one is not found, it then looks for an index template.

 

동적 페이지 띄우기

방금한 것은 정적 페이지를 그대로 화면에 띄운것이다. 반면 thymeleaf라는 템플릿 엔진을 사용하여 원하는대로 html 파일을 바꿀 수가 있다. 스프링 부트 매뉴얼 중 7.1.10 template engine을 확인하면 template engine으로  네가지를 제공해줌을 알 수가 있다.

우리는 이중에서 thymeleaf를 선택한 것이다. 이것을 이용해서 동작되는 화면을 이제 만들어보자.

webapplication에서 첫번째 진입점인 controller를 만들어야 한다. 일단 controller라는 패키지를 하나 만들고, 그 밑에 HelloController.java를 만든다.

 

그리고 자바 파일 안에 다음과 같이 작성하면, 웹 어플리케이션에서 /hello로 요청이 들어올경우 이 메서드를 실행한다. mvc에서 Model에 해당하는 model 객체에 addAttribute로 hello라는 문자열을 넘기고 hello라는 문자열을 리턴한다.

package com.heejin.hellospring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {
    @GetMapping("hello")
    public String hello(Model model) {
        model.addAttribute("data", "hello!!");
        return "hello";
    }
}

이제는 controller에서 넣어준 데이터를 받아 화면에 띄울 html파일을 만들어야 한다. resources/template 밑에 hello.html을 만든다. xml의 스키마로 tymeleaf.org  경로를 정해주면 thymeleaf를 사용할 수가 있다. 이때 ${data} 자리에는 HelloController의 hello 메서드에서 Model에 data 라는 이름으로 넣어준 "hello!!" 값이 들어가게 된다.

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
<p th:text="'안녕하세요. ' + ${data}">안녕하세요, 손님</p>
</body>
</html>

이것은 다음과 같은 원리로 이뤄진다. 웹 브라우저에서 localhost:8080/hello라는 이름으로 요청을 던지면 내장된 톰캣 웹서버에서 /hello만을 받아 스프링한테 던진다. 스프링은 Get 방식으로 해당 url과 매칭된 메서드가 실행되며 이 메서드에서 필요한 Model객체를 만들어 넣어준다. 메서드는 Model객체에 hello라는 값을 data라는 이름으로 넣어놓고 hello를 리턴하는데, 이것은 렌더링할 화면의 경로를 말한다. viewResolver는 이 리턴 값에 따라 templates/hello.html을 찾아 렌더링을 한다. 어떻게 이 뷰리졸버가 다음과 같이 스프링 부트 템플릿 엔진의 기본 viewName을 매핑하도록 설정되어있다.

즉, 'resources: template/+ {ViewName} + .html' 에서 {ViewName} 이 자리에 hello가 들어가는 것이다.

spring-boot-devtools 라이브러를 추가해주면 .html 파일을 컴파일만 해주면 서버 재시작 없이 View 파일 변경이 가능하다. 

빌드하고 실행하기

빌드된 파일 만들기

빌드하고 실행할 수 있는 파일을 만들어보자. 일단 intelliJ에서 실행했던 것들을 모두 꺼야만 빌드시 오류가 없다. 콘솔창에서 프로젝트 파일 경로에 가서 다음과 같이 실행하면 빌드 파일이 build라는 폴더 밑에 생긴다.

 ./gradlew build

여기서 이 build의 libs 라는 폴더에 들어가보면 .jar 파일이 생성된다. 이것이 자바로 실행할 수 있는 빌드 파일이다.

빌드 파일 실행하고 삭제하기

이 .jar 파일을 java로 java -jar 로 실행시킨다.

java -jar hello-spring-0.0.1-SNAPSHOT.jar

이렇게 하면 실제 서버에도 스프링이 동작한다. 과거에는 톰캣을 설치하고 war파일을 만들어 집어넣는 등의 복잡한 과정이 필요했지만 지금은 jar파일을 만들어서 그냥 실행만 하면 된다.

 

프로젝트 파일에서 다음과 같이 실행하면 .jar 파일을 포함한 build 폴더가 모두 삭제된다.

./gradlew clean 
// 혹은 
./gradlew clean build