/images/profile.png

eclipse์—์„œ spring-boot๋กœ web ๋งŒ๋“ค๊ธฐ

Spring ํ™˜๊ฒฝ์—์„œ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค๋ฉด pom.xml ์— ์ด๋Ÿฐ์ €๋Ÿฐ ์„ค์ •๋“ค์„ ์ ์–ด์ค˜์•ผ ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์ˆ˜๊ณ (?)๋ฅผ ๋œ์–ด์ค„์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ค‘์— ํ•œ๊ฐ€์ง€๊ฐ€ ๋ฐ”๋กœ Spring Boot๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ธ๋ฐ, ์ดํด๋ฆฝ์Šค ํ™˜๊ฒฝ์—์„œ ๋งŒ๋“œ๋Š” ๋ฒ•์„ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•œ๋‹ค.

new > Maven Project

๋นˆ Maven Project ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ์•„์ฃผ ๊ฐ„๋‹จํ•˜๋‹ˆ ์ƒ๋žตํ•˜๊ณ … ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด pom.xml ์€ ์•„๋ž˜์ฒ˜๋Ÿผ ์•„์ฃผ ๊น”๋”ํ•œ(?)์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋œ๋‹ค.

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>boot</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</project>

๊ทธ๋Ÿฌ๋ฉด ์ด ๋น„์–ด์žˆ๋Š” pom.xml ์— Spring-Boot ์— ํ•„์š”ํ•œ ์„ค์ •๋“ค์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ๋กœ ํ•œ๋‹ค.

<parent> <!--boot์˜ ์Šคํƒ€ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
    <relativePath />
</parent>

<dependencies>
    <dependency> <!--boot์—์„œ ์Šคํƒ€ํ„ฐํŒจํ‚ค์ง€๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ฒƒ๋“ค์ค‘์— web ์„ค์ • ๋ถ€๋ถ„ -->
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

๊ทธ๋‹ค์Œ ์ž„์˜์˜ java ํด๋ž˜์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  ๊ฑฐ๊ธฐ์— ์•„๋ž˜์ฒ˜๋Ÿผ ์„ค์ •ํ•˜๋ฉด ๋

@SpringBootApplication // @Configuration + @EnableAutoConfiguration + @ComponentScan ๋“ค์˜ ์ข…ํ•ฉ ์–ด๋…ธํ…Œ์ด์…˜
public class TestApplication{
    public static void main(String[] args) throws Exception {
        SpringApplication.run(TestApplication.class, args);
    }
}

Spring Boot ์—์„œ๋Š” ๋‚ด์žฅWAS๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— main ๋ฉ”์†Œ๋“œ์—์„œ ์šฐํด๋ฆญํ›„ run AS โ†’ Spring Boot App ์„ ์„ ํƒํ•ด์ฃผ๋ฉด 8080ํฌํŠธ๋กœ ๋„์›Œ์ง€๊ฒŒ ๋œ๋‹ค.

new > Spring Starter project

(STS๊ฐ€ ์„ค์น˜๋˜์–ด์žˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์—)์ด ๋ฉ”๋‰ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ„์—์„œ ํ–ˆ๋˜ ์ผ๋ จ์˜ ์„ค์ •๋“ค์„ ์ž๋™์œผ๋กœ ํ•ด์ฃผ๊ฒŒ ๋œ๋‹ค. ๊ฐ„๋‹จํ•œ ๋‚ด์šฉ์ด๋‹ˆ next๋ฅผ ํ•ด์ฃผ๋‹ค ๋งˆ์ง€๋ง‰์— Dependencies ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ Web ์„ ์ฒดํฌํ•ด์ฃผ๊ณ  Finish ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋

Spring Initializr (start.spring.io)

http://start.spring.io/ ์— ๋“ค์–ด๊ฐ€๋ณด๋ฉด ๊ตฌ์ง€ ์„ค๋ช…ํ•˜์ง€ ์•Š์•„๋„ ์นœ์ ˆํ•˜๊ฒŒ Generate ํ•ด์ฃผ๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ธ๋‹ค. ์—ฌ๊ธฐ์„œ web ์„ Dependencies์— ์ถ”๊ฐ€ํ•˜๊ณ  Generate๋ฅผ ํ•˜๋ฉด ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๊ฐ€ ์••์ถ•๋œ ์ƒํƒœ๋กœ ๋‹ค์šด์ด ๋ฐ›์•„์ง€๊ฒŒ ๋˜๊ณ  ์ด๋ฅผ IDE ์—์„œ ์—ด์–ด๋ณด๋ฉด ์œ„์—์„œ ํ–ˆ๋˜ ์ผ๋ จ์˜ ๊ณผ์ •๋“ค์ด ์„ค์ •๋˜์–ด ์žˆ๋Š”๊ฒƒ์„ ํ™•์ธํ•ด๋ณผ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

๋‚ด์žฅํ†ฐ์ผ“์„ ์‚ฌ์šฉ์•ˆํ•˜๊ณ  ๋ณ„๋„ ํ†ฐ์ผ“์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

Spring boot๋Š” ์ž์ฒด์ ์œผ๋กœ ๋‚ด์žฅ WAS๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ด€๋ฆฌํฌ์ธํŠธ๋‚˜ ์ด๋Ÿฐ์ €๋Ÿฐ ์ด์œ ๋กœ ๋‚ด์žฅํ†ฐ์ผ“์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ํ™˜๊ฒฝ์ด๋ผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ค์ •์„ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

  • ์ผ๋ฐ˜์ ์œผ๋กœ ๋นŒ๋“œ๊ฐ€ ๋˜๋ฉด jar๋กœ ๋งŒ๋“ค์–ด ์งˆํ…๋ฐ war๋กœ ๋นŒ๋“œ ๋˜๋„๋ก ์ˆ˜์ •์„ ํ•ด์•ผํ•œ๋‹ค. (was๊ฐ€ WAR๋ฅผ ๋ฌผ๊ณ  ๋– ์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ..)
<packaging>war</packaging>
  • dependency ์— tomcat์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>
  • ์•„๋ž˜์ฒ˜๋Ÿผ main ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์— SpringBootServletInitializer๋ฅผ ์ƒ์†๋ฐ›๊ฒŒ ํ•œ ํ›„ configure๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์ค€๋‹ค.
@SpringBootApplication
public class TestApplication extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(TestApplication.class);
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(TestApplication.class, args);
    }
}
  • ํ†ฐ์ผ“์— ๋„์šฐ๊ธฐ ์œ„ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์„ค์ •(Project Facets)์—์„œ Dynamic Web Module์„ ์ฒดํฌํ•ด์ค€๋‹ค.

์ฐธ๊ณ  URL

lombok(๋กฌ๋ณต)์†Œ๊ฐœ ๋ฐ ์„ค์น˜

์ผ๋ฐ˜์ ์œผ๋กœ ์ž๋ฐ”๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋ฉด Model ์„ ๋งŒ๋“ค๊ณ  ๊ฐ ๋ฉค๋ฒ„๋ณ€์ˆ˜๋ฅผ ์ ‘๊ทผํ• ์ˆ˜ ์žˆ๋Š” (๊ฐ ์š”์†Œ๋“ค์ด private ์ ‘๊ทผ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์„๋•Œ) method ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋œ๋‹ค. IDE์—์„œ ์ œ๊ณตํ•˜๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ… (์œˆ๋„์šฐ/์ดํด๋ฆฝ์Šค ๊ธฐ์ค€)

  • get/set ๋ฉ”์†Œ๋“œ : Alt + Shift + S + R
  • toString ๋ฉ”์†Œ๋“œ : Alt + Shift + S + S
  • ๊ธฐํƒ€ ๋“ฑ๋“ฑ…
public class Student {
    private int id;
    private String name;
    private int grade;
    private String department;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", grade=" + grade + ", department=" + department + "]";
    }    
}

์ด๋ ‡๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ ์–ด๋…ธํ…Œ์ด์…˜ ์„ค์ •์œผ๋กœ ์ ์šฉํ• ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์†Œ๊ฐœํ•˜๊ณ ์ž ํ•œ๋‹ค. ๋ฐ”๋กœ lombok, ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€ : https://projectlombok.org ์„ค์น˜ ๋ฐ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ์•„์ฃผ ๊ฐ„๋‹จํ•˜๋‹ค. ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ jar๋ฅผ ๋‹ค์šด๋ฐ›๊ณ  ์‹คํ–‰, ์•„๋ž˜์ฒ˜๋Ÿผ ์ดํด๋ฆฝ์Šค ์‹คํ–‰ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•ด์ค€๋‹ค์Œ์— ์ธ์Šคํ†จ์„ ๋ˆ„๋ฅด๋ฉด ๋œ๋‹ค. /images/lombok/lombok.png maven ํ™˜๊ฒฝ์—์„œ dependency๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹น์—ฐํžˆ ์ถ”๊ฐ€์„ค์ •์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version> <!--๋ฒ„์ „์€ ๊ทธ๋•Œ ๋งž์ถฐ์„œ-->
</dependency>

์‹ค์ œ๋กœ ์ฝ”๋“œ์ƒ์—์„œ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์ •๋ง ๊ฐ„๋‹จํžˆ, ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ์ ์šฉํ•ด์ฃผ๋ฉด ๋!

import lombok.Data;

@Data
public class Student {
    private int id;
    private String name;
    private int grade;
    private String department;
}

๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ์ ์ธ method๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค. /images/lombok/lombok-annotation.png ์ผ๋ฐ˜์ ์œผ๋กœ @Data๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ƒํ™ฉ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ์ง€์ •๋„ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.

logback ์„ค์ •ํ•˜๊ธฐ

์ž๋ฐ” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ํ•œ๋ฒˆ์ฏค์€ ๋“ค์–ด๋ดค๊ณ , ํ•œ๋ฒˆ์ฏค์€ ์‚ฌ์šฉํ–ˆ์„๋ฒ•ํ•œ logger ๋กœ log4j๊ฐ€ ์žˆ์„๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ์ตœ๊ทผ๋“ค์–ด logback์ด๋ผ๋Š”๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์—ˆ๊ณ , ์™œ logback์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋ผ๋Š” ๊ธ€์ด ์žˆ์„์ •๋„๋กœ ์—ฌ๋Ÿฌ ์ธก๋ฉด์—์„œ ๊ฐœ์„ ์ด ๋œ๋“ฏ ํ•˜๋‹ค. (๋งํฌ) ์ด๋ฒˆ์— ์ž‘์„ฑํ•  ๊ธ€์˜ ๋ชฉ์ ์€ logback์„ ์„ค์ •ํ•˜๊ณ  ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์ž‘์„ฑํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค. โ€ป ๊ณต์‹์‚ฌ์ดํŠธ : https://logback.qos.ch/

pom.xml

maven๊ตฌ์กฐ๋ผ๊ณ  ๊ฐ€์ •ํ–ˆ์„๋•Œ logback Dependency๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด pom.xml ์— ์„ค์ •ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.7</version> <!--๋ฒ„์ „์€ ์ƒํ™ฉ์— ๋”ฐ๋ผ -->
</dependency>

๋กœ๊ทธ๋ ˆ๋ฒจ

ERROR, WARN, INFO, DEBUG or TRACE

# logback ์„ค์ •ํŒŒ์ผ

์ผ๋ฐ˜์ ์œผ๋กœ logback.xml ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋งŒ๋“ค์–ด src/main/resources/์•„๋ž˜์— ์œ„์น˜ํ•˜๊ฒŒ ๋œ๋‹ค. Spring-Boot ํ™˜๊ฒฝ์—์„œ๋Š” logback-spring.xml ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š”๋ฐ logback.xml๋กœ ์„ค์ •ํ•˜๋ฉด ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ์„ค์ •ํ•˜๊ธฐ ์ „์— ๋กœ๊ทธ๋ฐฑ ๊ด€๋ จํ•œ ์„ค์ •์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ œ์–ดํ•  ์ˆ˜๊ฐ€ ์—†๊ฒŒ ๋œ๋‹ค. ( ๊ณต์‹์‚ฌ์ดํŠธ ๋ฉ”๋‰ด์–ผ : https://logback.qos.ch/documentation.html )

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />    

    <!-- ๋ณ€์ˆ˜ ์ง€์ • -->
    <property name="LOG_DIR" value="/logs" />
    <property name="LOG_PATH_NAME" value="${LOG_DIR}/data.log" />

    <!-- FILE Appender -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH_NAME}</file>
        <!-- ์ผ์ž๋ณ„๋กœ ๋กœ๊ทธํŒŒ์ผ ์ ์šฉํ•˜๊ธฐ -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH_NAME}.%d{yyyyMMdd}</fileNamePattern>
            <maxHistory>60</maxHistory> <!-- ์ผ์ž๋ณ„ ๋ฐฑ์—…ํŒŒ์ผ์˜ ๋ณด๊ด€๊ธฐ๊ฐ„ -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5p] [%F]%M\(%L\) : %m%n</pattern>
        </encoder>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5p] [%F]%M\(%L\) : %m%n</pattern>
      </layout>
    </appender>

    <!-- TRACE > DEBUG > INFO > WARN > ERROR, ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์•ˆํ•จ -->
    <!-- profile ์„ ์ฝ์–ด์„œ appender ์„ ์„ค์ •ํ• ์ˆ˜ ์žˆ๋‹ค.(phase๋ณ„ ํŒŒ์ผ์„ ์•ˆ๋งŒ๋“ค์–ด๋„ ๋˜๋Š” ์ข‹์€ ๊ธฐ๋Šฅ) -->
    <springProfile name="local">
      <root level="DEBUG">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
      </root>
    </springProfile>
    <springProfile name="real">
      <root level="INFO">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
      </root>
    </springProfile>
</configuration>

java ์ฝ”๋”ฉ์—์„œ์˜ ๋กœ๊น…

์‹ค์ œ ์‚ฌ์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด LoggerFactory๋ฅผ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Lombok์–ด๋…ธํ…Œ์ด์…˜์„ ํ™œ์šฉํ•˜๋ฉด ์‹ฌํ”Œํ•˜๊ฒŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • LoggerFactory ์‚ฌ์šฉ
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
    static final Logger logger = LoggerFactory.getLogger(Foo.class);

    public void test() {
        logger.debug("ID : {}", "foo");
    }
}
  • Lombok ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Foo {

    public void test() {
        log.debug("ID : {}", "foo");
    }
}

๋งˆ์น˜๋ฉฐ

์ผ๋ฐ˜์ ์ธ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” WAS์—์„œ ๋กœ๊น…์„ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— file ๋กœ ๋กœ๊น…์„ ํ•  ํ•„์š”๋Š” ์—†์„๊ฒƒ ๊ฐ™๋‹ค.(์ผ๋ฐ˜ jar ํ˜•ํƒœ์—์„œ๋Š” ํŒŒ์ผ ๋กœ๊น…์ด ํ•„์š” ํ• ์ˆ˜๋„…)

์ฐธ๊ณ ์‚ฌ์ดํŠธ

์ž๋ฐ” 8 Date

์ด์ œ๊นŒ์ง€ ๋‚ด ๊ธฐ์–ต์œผ๋กœ๋Š” Date ๊ด€๋ จ ํด๋ž˜์Šค๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ์ ์ฐจ ๋ฐ”๊ฟ”์จ์˜จ๊ฑธ๋กœ ๊ธฐ์–ต์ด ๋‚œ๋‹ค. java.util.Date > java.util.Calendar > org.joda.time ๊ทธ๋Ÿฐ๋ฐ java 8 ๋ฒ„์ „์—์„œ ๊ธฐ์กด์— ์žˆ์—ˆ๋˜ ๋ฌธ์ œ๋“ค์„ ๊ฐœ์„ ํ•ด์„œ ๋‚˜์™”๋‹ค๊ณ  ํ•œ๋‹ค. (๋„ค์ด๋ฒ„ HellowWorld ํฌ์ŠคํŒ… ์ฐธ๊ณ ) JSR-310 ์ด๋ผ๋Š” ํ‘œ์ค€๋ช…์„ธ๋กœ.

์ง€๊ธˆ๋ถ€ํ„ฐ๋Š” JAVA 8 ์—์„œ ์ œ๊ณตํ•˜๋Š” API๋กœ ๋‚ ์งœ ์—ฐ์‚ฐ์„ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ ์ž ํ•œ๋‹ค. (๋ฌผ๋ก  ์ˆ˜๋งŽ์€ ๋‚ ์งœ ์—ฐ์‚ฐ ๋ฐฉ๋ฒ•์„์ด ์žˆ์ง€๋งŒ ์ž์ฃผ ์“ฐ์ด๋Š” ๋ถ€๋ถ„๋“ค ์œ„์ฃผ๋กœ ์ •๋ฆฌํ•ด๋ณด์ž.)

  • Date > String (format)
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  • String > Date (format)
LocalDateTime.parse("2017-01-01 12:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  • ๋‚ ์งœ/์‹œ๊ฐ„ ์ฆ๊ฐ
LocalDateTime localDateTime = LocalDateTime.of(2017, 1, 1, 10, 0, 0);
localDateTime.plusDays(1);  // ์ผ
localDateTime.plusMonths(1);  // ์›”
localDateTime.plusHours(1); // ์‹œ๊ฐ„
localDateTime.plusWeeks(1); // ์ฃผ
localDateTime.minusYears(1);  // ๋…„
localDateTime.minusMinutes(1);  // ๋ถ„

๋” ๋‹ค์–‘ํ•œ ๋‚ด์šฉ๋“ค์€ ์•„๋ž˜ URL ์—์„œ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html

Spring Transactional ์„ค์ • ๋ฐ ์ฃผ์š”์†์„ฑ

์ง€๋‚œ๋ฒˆ์—๋Š” ํŠธ๋žœ์žญ์…˜์˜ ์„ค์ •๊ฐ’์— ๋Œ€ํ•ด ์•Œ์•„๋ณธ ๋ฐ” ์žˆ๋‹ค. [ Spring Transaction ์˜ต์…˜ ] ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‹ค์ œ๋กœ ์Šคํ”„๋ง ํ™˜๊ฒฝ์—์„œ ์–ด๋–ค์‹์œผ๋กœ ์„ค์ •ํ•ด์•ผ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ค ์†์„ฑ๋“ค์ด ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ ์ž ํ•œ๋‹ค.

์„ค์ •

๊ธฐ์กด xml๋ฐฉ์‹์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •์„ ํ•œ๋‹ค.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

ํ˜น, JavaConfig ๋ฐฉ์‹์œผ๋กœ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•œ๋‹ค.

@EnableTransactionManagement
public class AppConfig {
    ...

    @Bean
    public PlatformTransactionManager transactionManager() throws URISyntaxException, GeneralSecurityException, ParseException, IOException {
        return new DataSourceTransactionManager(dataSource());
    }
}

์œ„์™€๊ฐ™์ด ์„ค์ •์„ ํ•ด์ฃผ๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์„ค์ •ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ณณ ์–ด๋””์„œ๋“  @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•ด์„œ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

public class UserService{

  @Transactional
  public boolean insertUser(User user){
    ...
  }
}

์ฃผ์š”์†์„ฑ

@Transactional ์–ด๋…ธํ…Œ์ด์…˜์˜ ์ฃผ์š”์†์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์†์„ฑ์„ค ๋ช…์‚ฌ์šฉ ์˜ˆ
isolationTransaction์˜ isolation Level. ๋ณ„๋„๋กœ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด DB์˜ Isolation Level์„ ๋”ฐ๋ฆ„.@Transactional(isolation=Isolation.DEFAULT)
propagationํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ๊ทœ์น™์„ ์ •์˜ , Default=REQURIED@Transactional(propagation=Propagation.REQUIRED)
readOnlyํ•ด๋‹น Transaction์„ ์ฝ๊ธฐ ์ „์šฉ ๋ชจ๋“œ๋กœ ์ฒ˜๋ฆฌ (Default = false)@Transactional(readOnly = true)
rollbackFor์ •์˜๋œ Exception์— ๋Œ€ํ•ด์„œ๋Š” rollback์„ ์ˆ˜ํ–‰@Transactional(rollbackFor=Exception.class)
noRollbackFor์ •์˜๋œ Exception์— ๋Œ€ํ•ด์„œ๋Š” rollback์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Œ.@Transactional(noRollbackFor=Exception.class)
timeout์ง€์ •ํ•œ ์‹œ๊ฐ„ ๋‚ด์— ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ์ˆ˜ํ–‰์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ rollback ์ˆ˜ํ–‰. -1์ผ ๊ฒฝ์šฐ no timeout (Default = -1)@Transactional(timeout=10)

๋งˆ์น˜๋ฉฐ

์ž์นซ ์ž˜๋ชปํ–ˆ๋‹ค๊ฐ€๋Š” ์›์น˜์•Š๋Š” ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ• ์ˆ˜ ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ๊ฐ’์€ ์ˆ™์ง€ํ•˜๋Š”๊ฒŒ ์ข‹์„๊ฒƒ ๊ฐ™๋‹ค.

spring4์—์„œ json view ํ™œ์šฉํ•˜๊ธฐ(with @ResponseBody)

์ˆ˜๋งŽ์€ ๋ธ”๋กœ๊ฑฐ๋ถ„๋“ค์˜ ๋„์›€์„ ๋ฐ›๊ณ ์ž ๊ตฌ๊ธ€๋ง์„ ํ•ด์„œ ์ ์šฉ์„ ํ•ด๋ดค์ง€๋งŒ ๋„ˆ๋ฌด๋งŽ์€ ์‚ฝ์งˆ์„ ํ–ˆ๋‹ค.(ํ•ด๋ดค๋˜ ๋ฐฉ์‹์€ jsonViewResolver ๋ฅผ ๋”ฐ๋กœ ์„ค์ •ํ•ด๋ณด๊ฑฐ๋‚˜, @RequestMapping ์˜ต์…˜์„ ๋ฐ”๊ฟ”๋ณด๋Š” ์ˆ˜์ค€..) ํŠนํžˆ๋‚˜ Spring์„ค์ •๋ฐฉ์‹์ด ์˜ˆ์ „ ๋ฐฉ์‹์ด์˜€๋˜ xml์ด ์•„๋‹Œ javaconfig์˜€๊ธฐ ๋•Œ๋ฌธ์— ๋”์šฑ๋” ์ž๋ฃŒ๊ฐ€ ์—†์—ˆ๊ณ .. ํ•œ์ฐธ์„ ์‚ฝ์งˆํ•˜๋‹ค ํ•ด๊ฒฐ์„ ํ•˜์—ฌ ํฌ์ŠคํŒ…ํ•˜๊ฒŒ ๋œ๋‹ค. ์šฐ์„  ํ™˜๊ฒฝ์€ spring 4.3.4.RELEASE, Maven, jdk8์ž„์„ ๋ฐํžŒ๋‹ค.

pom.xml

jackson-mapper-asl์„ ์ด์šฉํ•ด์„œ ํ•˜๋ผ๋Š” ๋ธ”๋กœ๊ฑฐ๋“ค๋„ ์žˆ์—ˆ์ง€๋งŒ, ์•„๋ฌด๋ฆฌํ•ด๋„(๋ญ”๊ฐ€ Spring๋ฒ„์ „๊ณผ ๋งž์ง€ ์•Š๋Š”๋“ฏ ํ–ˆ๋‹ค.) ์ž˜ ์•ˆ๋˜์–ด ์•„๋ž˜์™€ ๊ฐ™์€ dependency๋ฅผ ์ฃผ์—ˆ๋‹ค.

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.5.1</version>
</dependency>

Controller

์•„๋ž˜์™€๊ฐ™์ด @ResponseBody ์–ด๋…ธํ…Œ์ด์…˜์„ ์„ค์ •ํ•ด์ฃผ๊ณ  ๋ฆฌํ„ด์€ ํ•ด๋‹น ๋ชจ๋ธ์„ ๋„˜๊ธฐ๋ฉด ๋œ๋‹ค.

@RequestMapping(value="/test")
@ResponseBody
public Map<String, Object> test(){
    	Map<String, Object> map = new HashMap<String, Object>();
    	map.put("1", "111");
    	map.put("2", 222);
    	return map;
    }

๊ทธ๋ฆฌ๊ณ  ํ˜ธ์ถœ์„ ํ•ด๋ณด๋ฉด ๊ธฐ๋Œ€ํ–ˆ๋˜๊ฒƒ์ฒ˜๋Ÿผ ์ด์˜๊ฒŒ jsonํ˜•ํƒœ๋กœ ๋‚˜์˜จ๋‹ค.

{
"1": "111",
"2": 222
}

๋ฌผ๋ก , list ๋‚˜ array, ์ผ๋ฐ˜ ๊ฐ์ฒด๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ •๋ฆฌ

์‚ฝ์งˆ์„ ๋์— ์•Œ๊ฒŒ๋œ ์‚ฌ์‹ค(?)์„ ์ •๋ฆฌํ•ด๋ณด์ž. ๋‹ค๋ฅธ์ธก๋ฉด์—์„œ ๋ถ„์„์„ ํ•ด๋ณด๋ฉด. @ResponseBody์„ ์ด์šฉํ•˜์—ฌ view ์— json ํ˜•ํƒœ๋กœ ๋‚˜ํƒ€๋‚ด๊ณ ์ž ํ•  ๊ฒฝ์šฐ ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์€ toString์œผ๋กœ ํ–ˆ์„๋•Œ jsonํ˜•ํƒœ๋กœ ๋‚˜์˜ฌ์ˆ˜ ์žˆ์œผ๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค. ์˜ˆ๋กœ๋“ค์–ด ์•„๋ž˜์ฒ˜๋Ÿผ ํด๋ž˜์Šค์— Lombok ์–ด๋…ธํ…Œ์ด์…˜์ธ @Data๊ฐ€ ๋ถ™์œผ๋ฉด ์ž๋™์œผ๋กœ toString์„ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ๋˜๋ฉด ์ž๋™์œผ๋กœ json ์ฒ˜๋ฆฌ๊ฐ€ ๋œ๋‹ค.

@Data
public Student{
  private String id;
  private String name;
  ...
}

@ResponseBody์„ ๋ถ™์ด๊ณ  List<Student>๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š”๋ฐ, ์ด๋Ÿด๊ฒฝ์šฐ ๋ณ„๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋˜์–ด json ํ˜•ํƒœ๋กœ ๋‚˜์˜ฌ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. (list.toString์„ ํ•˜๋ฉด jsonํ˜•ํƒœ๊ฐ€ ์•„๋‹Œ ์ด์ƒํ•œ ๋ฌธ์žํ˜•ํƒœ๋กœ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ… Map๊ฐ™์€๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€ ์ด์œ ๋กœ ๋ณ„๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ์ •์ƒ์ ์œผ๋กœ ๋‚˜์˜จ๋‹ค.)

๋งˆ์น˜๋ฉฐ

๋‹จ์ˆœํžˆ @ResponseBody๋ฅผ ์‚ฌ์šฉํ•ด์„œ json์œผ๋กœ ๋ฆฌํ„ดํ•˜๋ ค๋ฉด ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค ๋กœ ์ƒ๊ฐํ–ˆ๋˜๊ฒƒ์—์„œ, ์ด๊ฒƒ์ €๊ฒƒ ํ…Œ์ŠคํŠธ ํ•œ ๊ฒฐ๊ณผ toString์„ ํ• ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ณ  ๊ทธ ๊ฐ’์ด jsonํ˜•ํƒœ์ด๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค ๋กœ ๊ฒฐ๋ก ์ด ์ง€์–ด์กŒ๋‹ค. ํ™•์‹คํžˆ ์žฅ๋‹˜ ์ฝ”๋ผ๋ฆฌ ๋งŒ์ง€๋“ฏ์ด ‘๊ทธ๋Ÿฐ๊ฐ€๋ณด๋‹ค’ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ์‚ฝ์งˆ์ด ์ง„์งœ ๋ถˆํ•„์š”ํ•œ ์‚ฝ์งˆ์ด ๋˜๋Š”๊ฒƒ ๊ฐ™๋‹ค. ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด๊ณ , ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ด์„œ, ๊ฒฐ๋ก ์ ์œผ๋กœ ๋‚ด๊ฒƒ์œผ๋กœ ๋งŒ๋“œ๋Š” ์Šต๊ด€์„ ๊ฐ€์ ธ์•ผ ๊ฒ ๋‹ค.