스프링 프레임워크 실습 및 프로젝트

노요셉·2021년 2월 20일
0

위 블로그는 패스트캠퍼스 "Java 웹 개발 마스터 올인원 패키지 Online"강의를 정리한 것 입니다.

배경지식

OS X, linux에서 java, maven설치

sdkman를 사용해서 java 설치가능
참조: https://phoby.github.io/sdkman/

위에 블로그에 나온대로
SDMMAN!은 대부분의 유닉스 기반 시스템에서 여러가지 SDK(Software Development Kits)의 병렬 버전을 관리하기 위한 도구입니다.

이전에 오라클에서 설치한 java1.8 버전이 있는데 또 설치해봄
https://devkimgoon.tistory.com/3

java 특정 버전으로 설치

sdk list로 조회

# 설치할 Java 버전 조회
$ sdk list java

# 설치할 버전으로 설치
$ sdk install java 11.0.10.j9-adpt

심볼릭 링크로 사용할 자바 버전을 가리킴.

intellij에서 SDK JDK 경로 설정하기

ln -s ~/.sdkman ~/sdkman
https://cjred.net/2019-07-14-intellij-sdkman-jdk/


command + ;

sdk 설정

maven 특정 버전으로 설치

# 설치할 Java 버전 조회
$ sdk list maven

$ sdk install maven 3.6.1

maven, logback

maven : 빌드 담당
logback : 로그 담당

java hello world

에디터 없이 java 컴파일, 빌드 해보기

cd <프로젝트 디렉토리 생성할 PATH>
mkdir helloMaven && cd helloMaven
touch Main.java && vi Main.java

Main.java

class Main {
    public static void main(String [] args) {
        System.out.println("hello world");
    }
}

컴파일 javac Main.java
실행 java Main

maven은 디렉토리 구조가 정해져있음

Main.java 파일에 있는 Main 클래스를 패키지안에 넣어줍니다.
패키지는 디렉토리로 구분되고 패키지 이름의 규칙은 "도메인의 역순"

mkdir com
cd com
mkdir playground
cd playground
mkdir yobs
cd yobs
#다시 프로젝트 루트 디렉토리로 돌아와서 Main.java 복사함
cp Main.java ./com/playground/yobs/

Main.java 파일 수정 패키지를 입력해줌

package com.playground.yobs;
class Main {
    public static void main(String [] args) {
        System.out.println("hello world");
    }
}

다시 컴파일 해주고, java 실행시 classpath를 루트디렉토리로 지정하고 실행하면 hello world가 찍힘.

javac com/playground/yobs/Main.java
java -cp . com.playground.yobs.Main
hello world

java -jar 옵션 사용시 -cp 옵션은 무시됌
https://aterilio.tistory.com/75

클래스가 여러개 만들어지기 때문에 jar로 클래스들을 묶음
jar -> 아카이빙 목적 여러가지 파일을 하나의 파일로 묶어서 관리용이
-c : create
-f : file명

jar -cf helloMaven.jar com/*

jar <option> jar파일명 jar으로 묶일 패키지

java -jar helloMvn.jar
helloMvn.jar에 기본 Manifest 속성이 없습니다.

manifest.txt를 생성하고 다음과 같이 생성합니다.

vi manifest.txt
Main-class: com.playground.yobs.Main

jar 파일 추가
jar -cmf manifest.txt helloMvn.jar com/*
jar 파일 실행
java -jar helloMvn.jar

logback 설치

자바 로깅 라이브러리
이건 패스
이건 스프링에서 사용하는 log 라이브러리인 logback를 사용하기 위해
logback-classic.jar, logback-cor.jar, slf4j-api.jar 등을 설치해서 lib폴더에 넣고

Main.java에서 slf4j import 해서 LoggerFactory로 logger 만들고 logger로 info레벨의 로그를 찍어보는 것

jar로 묶으려면
manifest.txt 파일을 수정해줘야함
manifest 파일에 라이브러리를 추가해주면 됌. 마지막에 new line 꼭 넣어야함.
Class-path: lib/logback-classic.jar lib/logback-core.jar lib/slf4j-api.jar

jar 파일 수정
jar -cmf manifest.txt helloMvn.jar com/*

그리고 이렇게 의존성을 추가하고, xml로 되어있는 설정파일들을 추가할 수 있음.
설정파일을 추가함으로써 logback의 경우 로그 format, 로그 레벨들을 설정파일로 관리할 수 있음.
로거를 info레벨로 해두었는데, 설정파일에서 error레벨로 해두었다면, 앱을 실행할때 info로그는 error보다 낮은 단계의 로그이기에 찍히지 않을거에요.

결론

이렇게 cli 환경에서 java 컴파일, 의존성 추가, 패키지 생성, 적용, java class파일을 실행하는 걸 해보고 jar로 아카이빙 했는데 엄청 귀찮다는것을 알 수 있음.

maven을 통해 build, 아카이빙, 의존성 추가해보기

https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html

maven은 디렉토리 구조가 정해져있어요.

pom.xml 파일 생성
위에 사이트에 나와있는 pom.xml 내용을 복붙합니다.
groudId만 내 프로젝트 패키지에 맞게 수정해줍니다.
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.playground.yobs</groupId>
  <artifactId>helloSpring</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
  </dependencies>
</project>

pom.xml이 존재하는 프로젝트 루트 디렉토리에서 다음과 같이 해줍니다.
mvn validate
빌드 success가 나오면 pom.xml이 문제가 없는지 판단할 수 있습니다.

디렉토리 구조를 다음과 같이 만들었습니다.

.
├── Main.class
├── Main.java
├── helloMvn.jar
├── manifest.txt
├── pom.xml
└── src
    ├── main
    │   └── java
    │       ├── playground
    │       │   └── yobs
    │       │       ├── Main.class
    │       │       └── Main.java
    │       └── resources
    └── test
        └── java
            └── resources

mainfest.txt는 maven이 자동으로 만들어 줄겁니다. 제거해줍니다.
logback과 같은 라이브러리를 설치하기 위해서 maven central 에서 logback-classic 검색후 버전을 알아냅니다.
pom.xml의 의존성을 추가해줍니다.


여길 복사해서 pom.xml에 dependencies사이에 추가해주면 됩니다.

maven으로 컴파일을 해줍니다. 그러면 의존성 다운로드 받고, 빌드해줍니다.
mvn compile
build가 완료되면 target 폴더가 생성되고, 그 안에 빌드된 내용을 확인할 수 있습니다.
mvn clean을 통해 target폴더를 제거할 수 있습니다.

jar파일을 넣지 않았는데, 어떻게 maven에서 jar파일을 인식하냐?
home 디렉토리에 .m2 디렉토리에 의존성들이 받아진 것을 볼 수 있음.
maven은 .m2 디렉토리에 모든 의존성들을 받아놓고, pom.xml에서 필요한 jar파일을 불러다 빌드합니다.

executable jar를 만들기 위해서는 maven plugin을 설치해야함.
maven shade를 사용할 것임
https://javacan.tistory.com/entry/mavenshadeplugin

shade 사용법

빌드 부분 설정을 pom.xml로 복붙합니다.
이렇게 하면 shade가 동작하게만 되고

maven 사이트에 executable jar에 아래 부분을 pom.xml에 복붙해줍니다.

그러면 pom.xml이 이렇게 됩니다. mainClass부분을 내 프로젝트 맡게 바꿔줍니다.

<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.playground.yobs</groupId>
  <artifactId>helloSpring</artifactId>
  <version>1.0-SNAPSHOT</version>
 
  <properties>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.3.0-alpha5</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <configuration>
          <!-- put your configurations here -->
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>com.playground.yobs.Main</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  
</project>

mvn package로 프로젝트 jar로 아카이빙 합니다.

mvn compile : javac를 이용해서 class file만 생성
mvn package : jar로 아카이빙 해줌.

target 파일에 가보면 jar 파일 생성된거 볼 수 있음.

 solarconnect-yosephnoh  ~/Documents/practices/fastcampus/helloMvn  java -jar target/helloSpring-1.0-SNAPSHOT.jar
00:41:20.241 [main] INFO com.playground.yobs.Main - Hello world

database(h2), jdbc

https://developerhive.tistory.com/34

자바로 작성된 관계형 데이터베이스.
장점 : 간단하게 설치 및 실행, DB client 설치없이 web에서 SQL문 작성해서 실행해볼 수 있음.
file 모드 -> 간단하게 데이터베이스 실행을 멈춰도 데이터 남길 수 있음.
memory 모드 -> 데이터베이스 실행을 멈추면 데이터 날아감.

h2 database> features compatibility -> h2 ANSI SQL(표준)을 지원하고 다른 데이터베이스와 호환성을 갖음
h2 데이터베이스로 테스트 코드를 작성해서 테스트했으면 mysql에서도 동작한다는 얘기 -> 테스트 용이

jdbc: jdbc 만들어지기 전에는 mysql, oracle, mssql 등 관계형 데이터베이스를 만드는 벤더들마다 자바로 db 관련 프로그래밍에 필요한 jar파일을 각각 제공했었음.
java 1.1부터는 jdbc라는 interface를 이용하여 다양한 DB에서 하나의 interface를 통해 개발을 할 수 있게 제공되었음.

jdbc interface를 기반으로 mybatis, hibernate 같은 도구들이 생겨나게 되었음.

jdbc는 자바 스펙에 있기 때문에 따로 의존성 추가할 필요가 없음.

프로젝트 내비게이션에서 External Libraries에서 java.sql 패키지를 볼 수 있음.
이 패키지의 classes, interfaces들을 이용해서 jdbc를 이용해서 db 관련 프로그래밍을 할 수 있음.


h2 database 다운로드
https://www.h2database.com/html/download.html

압축풀고 h2.sh 실행
permission denied 발생하면 -> chmod u+x h2.sh
터미널켜서 cd <targetDirectory>/bin
./h2.sh

file 모드로 사용하기 위해서
jdbc url에 다음과같이 프로토콜 맞춰주고, db를 저장할 파일 path도 명시해줍니다.

h2 데이터베이스에서 table 생성하기

CREATE TABLE member (
  id int auto_increment,
  username varchar(255) not null,
  password varchar(255) not null,
  primary key(id)
);

test 데이터 추가

insert into member (username, password) values ("yobs", "1234");

jdbc 프로그래밍

class Main {
    public static void main(String [] args) {
      Logger logger = LoggerFactory.getLogger(Main.class);
      logger.info("Hello world");

        // DB와 연결
        Connection connection = null;
        Statement statement = null;
        // 각각의 DB 벤더들이 제공하는 Driver 로드
        try {
            Class.forName("org.h2.Driver");
            // 접속할 DB url MySQL 호환모드 설정 MySQL DB를 h2에서 사용가능함
            String url ="jdbc:h2:~/data/fastcampus;MODE=MySQL;";
            // 커넥션 객체 생성
            connection = DriverManager.getConnection(url, "sa", "");
            // DB에서 SQL문 실행하기 위해 statement 작성
            statement = connection.createStatement();

            // ResultSet 인터페이스를 이용해서 record단위 데이터를 참조할 수 있음.
            ResultSet resultSet = statement.executeQuery("select id, username, password from member");
            // next 메서드를 시작으로 cursor가 첫번쨰 row부터 참조하게 됌.
            while(resultSet.next()){
              int id = resultSet.getInt("id");
              String username = resultSet.getString("username");
              String password = resultSet.getString("password");

              logger.info("id" + id + ", username" + username + ", password" +password);
            }


        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        finally {
            // 사용한 resource 반환
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

인텔리제이 Edit Configuration Intellj: Module not specified

https://medium.com/@ufamily/intellij-module-not-specified-351c2964928a

실행하려는 프로젝트 artifact 선택해주고 JDK 선택해주면 됌.


h2 memory 모드로 사용

커넥션 url 수정

String url ="jdbc:h2:mem:fastcampus;MODE=MySQL;";

DDL 추가, 트랜잭션 추가

class Main {
    public static void main(String [] args) {
      Logger logger = LoggerFactory.getLogger(Main.class);
      logger.info("Hello world");

        // DB와 연결
        Connection connection = null;
        Statement statement = null;
        // 각각의 DB 벤더들이 제공하는 Driver 로드
        try {
            Class.forName("org.h2.Driver");
            // 접속할 DB url MySQL 호환모드 설정 MySQL DB를 h2에서 사용가능함
            String url ="jdbc:h2:mem:fastcampus;MODE=MySQL;";
            // 커넥션 객체 생성
            connection = DriverManager.getConnection(url, "sa", "");
            // DB에서 SQL문 실행하기 위해 statement 작성
            statement = connection.createStatement();

            // 트랜잭션 설정 -> commit이란 명령어가 실행되야 DB에 저장됌.
            connection.setAutoCommit(false);

            // memory 모드로 사용하기에 DDL를 추가해줌
            statement.execute("create table member(id int auto_increment, username varchar(255) not null, password varchar(255) not null, primary key(id))");
            statement.executeUpdate("insert into member(username, password) values('yobs', '1234')");


            // ResultSet 인터페이스를 이용해서 record단위 데이터를 참조할 수 있음.
            ResultSet resultSet = statement.executeQuery("select id, username, password from member");
            // next 메서드를 시작으로 cursor가 첫번쨰 row부터 참조하게 됌.
            while(resultSet.next()){
              int id = resultSet.getInt("id");
              String username = resultSet.getString("username");
              String password = resultSet.getString("password");

              logger.info("id:" + id + ", username:" + username + ", password:" +password);
            }
            // commit 실행
            connection.commit();

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
            try {
                // rollback 추가
                connection.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        finally {
            // 사용한 resource 반환
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

db커넥션 및 CRUD기능하는 코드에서 try 문이 복잡한데,
connection.close, statement.close등 close관련기능 때문에 try catch가 많이 보이는데, close에서 exception을 던지기 때문에 try catch문을 넣어줘야하는데 java7이후부터는 close가 필요한 부분에서는 AutoClosable이란 인터페이스를 추가해서 AutoClosable을 상속받은 객체들은 자동으로 close메서드가 호출됌.

intellij에서 action사용 ( cmd + shift + a ) : contructor나 override Method등 바로 할 수 있음.

java11에서는 로컬 타입 추론
var url = "jdbc:h2:mem:test;MODE=MySQL;"; value가 String이기 때문에 알아서 타입추론해버림.

Maven

https://goddaehee.tistory.com/199

lombok

반복작업 제거

servlet

'servlet' 구글링
wikipedia: 자바 서블릿은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 '서블릿'으로 불림.
(왠만하면 영어버전 wiki, 모든 구글링은 영어로 보는게 좋음. 정보가 최신임)

JavaEE, JavaSE
JavaEE를 사용하려면 JavaSE에서 dependency 추가해야함.
먼저 JavaEE 검색 > JavaEE api spec > javax.servlet
Package javax.servlet에 interface들이 나오는데 이 spec를 각 벤더들(Apache tomcat, Oracle Weblogic, Tmax jeus)이 implements하고 우린 그걸 dependency로 사용하여 웹 애플리케이션을 개발하게 됌.

servlet도 interface이므로 'servlet spec'으로 구글링 > Oracle Servlet specification 문서 볼 수 있음.

레퍼런스:
https://codevang.tistory.com/191
https://jeong-pro.tistory.com/222

servlet filter

예를 들어 interceptor같은 servlet동작 앞뒤로 원하는 기능을 넣고싶을때 사용하는 기능, 주로 로그인한 사용자 인증처리를 여기서 처리함.

차이점 Interceptor는 스프링 컨테이너에 등록, filter는 서블릿 컨테이너에 등록

filter도 servlet 등록하고, 매핑하는 것 처럼 web.xml에 비슷하게 설정을 해둬야함.

javax.filter는 interface이기 때문에 implements를 하고 doFilter만 override해주면 됌.

web.xml

web.xml이란?
web.xml 작성 방법
web.xml 작성되는 내용
servlet 설정

출처: https://m.blog.naver.com/ahyun1245/221558458502

jar와 war

레퍼런스: https://programmer93.tistory.com/40

스프링 프레임워크 코어

overview

spring.io > projects > spring framework > learn > reference Doc
레퍼런스 문서

Java community process를 통해 자바의 기능이 추가되면서 기능 개선이 되고 있음.

servlet이 JavaEE 스펙이므로 JavaEE로 개발해야함.

the Ioc Container

spring framework는 Ioc 원칙을 구현함.

Application Context 이 인터페이스를 통해 IoC 를 지원.

'ApplicationContext api' 구글링
org.springframework.ApplicationContext interface가 스프링 프레임워크 기저에 동작하고 있고요.

POJOs는 개발자가 비즈니스 로직을 구현한 소스코드에 스프링에서 제공해주는 기능을 사용하기위해 개발자가 configuration metadata를 넣으면 스프링컨테이너가 동작한다는 것입니다.

그러면 configuration을 하는 방법이 어떤게 있는가?
XML, Annotation-based configuration도 있습니다.

스프링 프레임워크에서는 "Bean"이란 것을 만듭니다. 쉽게 생각하면 객체이고, 이 객체들의 라이프사이클을 관리하는게 "Ioc Container"입니다.

Bean overview

bean overview

spring Ioc container는 하나 이상의 bean들을 관리합니다. configuration metadata로 인해 bean들이 생성됩니다.
설정하는 내용은 패키지 name, 다른 bean에 대한 reference, bean에 대한 pool size를 정할 수도 있고, bean의 scope, lifecycle callbacks 등등이 있음.

  • naming beans
    모든 bean들은 하나 이상의 식별자가 필요함.
  • instantiation with a Constructor

Ioc Container Dependencies

애플리케이션에서는 하나 이상의 객체로 구성되죠. 그 객체들이 상호작용함으로써 애플리케이션이 동작하는거죠.

tight한 의존관계란 보통 a,b 클래스가 있고, a가 b를 의존하는 상태에서 b의 코드가 수정됨에 따라 a 코드가 수정되야하는 상황을 말합니다.
기능의 확장을 위해 외부에서 b클래스의 초기값을 설정하려면 a를 거쳐 b까지 영향이 가게됩니다. -> 복잡도가 높아집니다.

이러한 의존관계를 푸는걸 보통 decoupling이라고 합니다.
a클래스 내부에서 b객체를 선언하지 않고, a객체와 b객체를 각각 생성해서 a의 객체를 생성할때 b객체를 인자로 넣어주면 의존관계가 줄어들게 됩니다. 의존성을 주입하는 방식으로,a를 거쳐 b까지 의존하는관계를 끊어버리게 되는겁니다. 이 DI(의존성 주입)을 framework에 접목시킨 것이 "IOC container"

이렇게 함으로써 이점은 객체들간의 관계를 configuration metadata로 볼 수 있고, 비즈니스 코드, 비즈니스 로직에만 집중할 수 있다.
또한, 테스트에도 용이함.

  • Contructor-based or setter-based DI?
    spring은 contructor-based를 권장합니다.

  • Dependency Resolution Process
    ApplicationContext가 xml, java code, or annotation를 통해 configuration metadata를 읽어서 beans에 대한 설정을 초기화하고

  • Circular dependencies
    a -> b, b -> a 서로 참조하는 관계일 경우, 이런 경우에는 에러가 발생함.

  • 기존 database access dao 문제점

  • 의존성 만드는 순서도 조작할 수 있음.
    1.4 Dependencies > 1.4.3 Using depends- on

  • bean을 사용하느 시점에서 init 하기
    1.4 Dependencies > 1.4.4 Lazy-initialized Beans
    어떻게 확인? lazy-init=true 값을 줌으로써 디버깅으로 실행해보면
    Bean 내부에 메서드가 호출될때 생성자 호출됌.

  • Autowiring Collaborators
    xml로 사용할때는 Autowired 비권장

Bean Scopes

객체를 만들때 컨테이너가 객체를 만들때 한번만 생성자 호출하는지? 호출할때마다 객체를 계속 생성하는지?

singleton, prototype만 알고 넘어감.
singleton: ioc 컨테이너에서 단 하나의 인스턴스만 생성
prototype: ioc 컨테이너에서 객체 생성할때 매번 만듬

bean의 scope에 "prototype"을 명시해주면 됩니다.
singleton이 default scope입니다.

junit5 추가하기 'junit' 구글링 > 공식사이트 user Guide >
4. Running Tests > 4.2 Build support > 4.2.2 Maven >
Configuring Test Engines

  • 객체의 Identity, Equals
    동일성(identity): 객체 주소 같다. (obj1 == obj2)
    동등성(equals): 객체의 값이 같다. (obj1.equals(obj2))

'java object api' 구글링 > oracle 공식 홈페이지 > method summary
Object 클래스는 모든 클래스의 부모 클래스 -> 모든 클래스는 equals란 메서드를 갖고 있고, hashCode라는 메서드를 동일성을 갖기 위해 override하고 있음.
IDE를 intellij 로 사용한다면 generate > equals() and hashcode() 해주면 됌.

클래스에
@EqualsAndHashcode를 붙여도 됩니다. (lombok 기능)


AOP

https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/

wiki aop
https://ko.wikipedia.org/wiki/%EA%B4%80%EC%A0%90_%EC%A7%80%ED%96%A5_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

횡단 관심사의 분리를 허용함으로써 모듈성을 증가시키는 것이 못적인 프로그래밍 패러다임.

try catch rollback 과 같은 코드들을 소스코드에 넣지 않고, 반복적인 코드를 외부에서 관리함으로써 반복되는 코드를 줄이고 코드 관리가 더욱 편리해짐.

용어 정리

@WebServlet

profile
서로 아는 것들을 공유해요~

0개의 댓글