Spring

[Spring] 스프링 Web MVC 프로젝트 시작하기 (feat. maven, gradle의 차이)

랩실외톨이 2023. 7. 15. 21:09
반응형

이 포스팅은 Spring 프로젝트 생성과 파일구조에 대해 다룹니다.
 
그에 앞서 maven, gradle이 도대체 무엇일까요?
 
maven과 gradle은 자바 프로젝트를 빌드할 수 있는 도구입니다. 그렇다면 이들은 무슨 차이가 있을까요?
 
 
 
Maven의 장점 및 특징
 

  • 컴파일, 빌드를 동시에 수행
  • pom.xml을 사용해 의존성을 따로 관리하기 때문에 유지보수가 용이함

 
 
 
그렇다면 Maven의 단점은 무엇일까요?
 

  • xml로 정의하기 때문에 이해하기 쉬울 수 있으나 설정 내용이 길어지고 가독성이 떨어짐
  • 특정 플러그인 설정을 소수의 모듈에서 공유하기 위해서는 부모 프로젝트를 생성하여 상속하게 해야 해서 복잡하고 사용이 어려워 유지보수성이 떨어짐

 
 
 
 

 
 
 
 
 
 
 
그렇다면 Gradle의 장점과 특징은?
 

  • Groovy 언어를 이용하여 코드로서의 설정정보를 구성하기 때문에 구조적임
  • 이미 반영된 빌드의 부분은 더 이상 재실행되지 않기 때문에 빌드 시간이 매우 단축됨

 
그렇다면 Maven과 비교해서 Gradle의 차이점은?
 

  • xml의 구조적인 틀을 벗어나 코딩에 의한 간결한 정의가 가능하다
  • 설정주입방식으로 정의하기 때문에 Maven의 상속 구조보다 재사용에 용이하다

 
Gradle은 Maven보다 나중에 나왔기 때문에 Maven의 단점을 커버한다는 특징이 있습니다.
그렇지만 아직도 Maven 역시 널리 쓰이는데 그 이유는 Maven 또한 편리한 도구이기도 하고
이미 Maven으로 짜여진 프로젝트들이 많아 레거시적인 측면도 있고, 러닝커브에 대한 비용적인 측면도 있다고 합니다.
 
 
그럼 먼저 Maven으로 프로젝트를 생성해 보겠습니다. 저는 인텔리제이를 사용할 거지만 이클립스, STS를 사용해도 상관없습니다.
 
 
 
 
 

 
 
 
 
IntelliJ에서 File > New > Project... 를 고르면 다음과 같은 화면이 뜹니다. (Mac 기준)
 
 

maven 프로젝트 생성

 
 
Name: 프로젝트 이름
Archetype: org.apache.maven.archetypes:maven-archetype-webapp
이외에도 JDK 버전을 맞춰서 선택해줍니다.
 
 
 
프로젝트를 생성하고 나면 pom.xml이란 파일이 생깁니다.
pom.xml에 Spring 설정을 넣어서 Spring 프로젝트로 만들어주겠습니다.
의존성들이 모여있는 <dependencies>에 아래 코드를 넣어줍니다.
 
 

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>5.3.9</version>
</dependency>

 
 
그리고 maven 빌드를 하고 나면 spring 프로젝트가 됐습니다!
 
 
 
참고로 이 의존성들을 쉽게 찾고 넣을 수 있는 사이트가 있습니다.
 
https://mvnrepository.com
 
 
이 사이트에 들어가서 쓰고자 하는 라이브러리를 검색하면 버전, 빌드 도구별로 코드들이 나와있습니다. 복붙하기만 하면 됩니다.
 
 
 
 

 
 
 
 
 
 
 
그럼 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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>demo</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>demo Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.9</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>demo</finalName>
  </build>
</project>

 
 
<name>: 프로젝트 이름
<url>: 프로젝트 사이트 url
 
 
 
프로젝트에서 사용하는 라이브러리들의 설정들을 끌어다가 pom.xml에 저장하게 되는데 이를 의존성을 설정한다고 합니다.
다음은 의존성에 대해 살펴보겠습니다.
 
 
<dependencies>: 이 프로젝트에 의존하는 다른 프로젝트 정보
<dependency>: 프로젝트 pom 정보
<groupId>: 프로젝트 그룹 ID
<artifactId>: 프로젝트의 artifact ID
<version>: 프로젝트 버전
<scope>: 의존하는 범위를 설정 ex) 위의 junit의 경우 test 패키지 한정
 
 
이렇게 의존성을 넣고 빌드 돌린 것들이 프로젝트에 저장된 걸 확인할 수 있는 곳이 있습니다.
 
 
 
 

라이브러리

 
 
 
외부에서 의존성 설정을 통해 다운로드한 라이브러리들입니다.
pom.xml에 juit과 spring의 의존성 설정한 것들이 한 곳에 모여있는 것을 알 수 있습니다.
 
 
 
 

 
 
 
 
 
 
이제 Maven 프로젝트 파일 구조에 대해서 살펴보겠습니다.
그전에 main 폴더 안에 java 폴더를 생성해 줬습니다. (국룰임)
그리고 test 폴더를 만들고 그 안에 java, resources 폴더를 생성해줬습니다. (국룰2)
 
 
 

 
 
 

  • src/main/java: 앞으로 작성할 코드들은 전부 이 java 폴더 안에 작성합니다.
  • src/main/resources: 프로퍼티나 xml 등 리소스 파일이 위치합니다.
  • src/main/webapp: 웹 어플리케이션 관련 파일이 위치합니다. ex) jsp, web-inf 폴더 등
  • src/test/java: 테스트 자바 소스 파일이 위치합니다.
  • src/test/resources: 테스트 과정에서 사용되는 리소스 파일이 위치합니다.

이렇게 Maven 프로젝트를 생성하고 구조를 살펴봤습니다.
 
 
 
 

 
 
 
 
이번에 Gradle 프로젝트를 생성해 보겠습니다.
 
 
 

Gradle 프로젝트 생성

 
 
 
 
프로젝트를 생성하고 나면 파일들이 생기는데 Maven에서 pom.xml 역할을 하는 것이 바로 build.gradle입니다.
build.gradle에 동일하 mvnrepository에서 코드를 가져와 spring-webmvc 라이브러리를 가져와보겠습니다.
 

implementation 'org.springframework:spring-webmvc:5.3.9'

 
 
그럼 build.gradle 파일을 보겠습니다.
 
 
 

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-webmvc:5.3.9'

    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

test {
    useJUnitPlatform()
}

 
 
 
repositories: 의존 모듈을 메이븐 중앙 리포지토리에서 다운
dependencies: 의존하는 외부 라이브러리 프로젝트 정보
implementation: 의존성 선언
 
 
 implementation vs complie
예전엔 implementation대신 complie이 사용됐는데 compile은 해당 의존성이 직접적으로 프로젝트에 추가되는 동시에, 의존성이 가지고 있는 다른 의존성도 프로젝트에 전이적으로 추가됩니다.
하지만 implementation은 해당 의존성만 프로젝트에 추가되며, 의존성이 가지고 있는 다른 의존성은 전이적으로 추가되지 않습니다.
implementation이 더 최신버전으로 권장된다고 합니다.
 
프로젝트 구조는 Maven과 크게 다르지 않기 때문에 생략합니다.
 
 
 

 
 
 
 
 
그럼 프로젝트가 잘 생성됐는지 테스트하기 위해 테스트 코드를 작성하고 프로젝트를 돌려보겠습니다.
 
 
Test.java
 

public class Test {
  private String s;

  public String getS() {
    return s;
  }

  public void setS(String s) {
    this.s = s;
  }
}

 
Test라는 객체를 만들어 줍니다.
안에는 String형인 s라는 변수가 선언되어 있고, getter, setter만 정의되어 있습니다.
 
 
AppContext.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppContext {
  @Bean
  public Test test() {
    Test t = new Test();
    t.setS("Spring 프로젝트에요!!");
    return t;
  }
}

 
Spring에는 DI 컨테이너에서 관리하는 Bean이라는 개념이 있습니다.
Bean은 한 마디로 Spring 어플리케이션을 구성하는 핵심 객체입니다.
위의 파일은 그 Bean을 등록하는 방법입니다.
 
@Configuration: Bean을 수동으로 등록할 때 클래스에 붙여주는 어노테이션. @Bean어노테이션이 붙어있는 메소드를 Bean으로 등록해 주며 싱글톤이 되도록 보장해 줍니다.
@Bean: 수동으로 Spring 컨테이너에 Bean을 등록하는 어노테이션.
 
이외에도 @Component라고 자동으로 Bean을 등록해 주는 어노테이션이 있습니다.
 
위의 코드를 통해 AppContext 클래스에 있는 test메소드를 bean으로 등록했습니다.
 
 
 
 

 
 
 
 
Main.java
 

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
      AnnotationConfigApplicationContext tx = new AnnotationConfigApplicationContext(AppContext.class);
      Test t = tx.getBean("test", Test.class);
      String str = t.getS();
      System.out.println(str);
      tx.close();
    }
}

 
AnnotationConfigApplicationContext: 자바 어노테이션을 이용한 클래스로부터 객체 설정 정보를 가져옴
getBean: Test.class에서 "test"라는 이름의 빈을 가져옴
 
이 메인 클래스를 돌리면 프로젝트가 실행됩니다. 그 결과는?
 
 
 

 
 
 
 

 
성공입니다!
출력문이 아주 잘 출력됐습니다.
 
 
 

 
 
 
+
 
그렇다면 위에서 말한 "싱글톤"이란 무엇일까요?
 
빈의 Scope에는 싱글톤과 프로토타입이 있습니다. (그 외에도 더 있지만 대표적인 두 가지만 다룹니다.)
 

  • Singleton: 하나의 Bean에 대해 Spring IoC 컨테이너 안에 단 한 개의 객체가 존재하는 것
  • Prototype: 하나의 Bean에 대해 다수의 객체가 존재할 수 있는 것

 
 
그렇다면 Main의 코드를 다음과 같이 수정해 봤습니다.
 

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
      AnnotationConfigApplicationContext tx = new AnnotationConfigApplicationContext(AppContext.class);
      Test t1 = tx.getBean("test", Test.class);
      Test t2 = tx.getBean("test", Test.class);
      System.out.println(t1 == t2);
      tx.close();
    }
}

 
 
 
 
그 결과는?
 

 
t1, t2는 각각 다른 변수이지만 싱글톤이기 때문에 결괏값은 true가 나옵니다.
getBean이 객체값을 가져오기 때문입니다.
 
 
 

출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문

반응형