ㅣ강의 소개 그리고 Maven이란
Spring 기초 강의다.
스프링 부트 프로젝트를 만들어보겠다.
강사 소개 : 해리
- 현 싱가포르 거주중, 자산 운용 개발 팀장
- 판교에서 일함.
- 실리콘밸리에서도 일함.
학습 목표
- Spring Framework 핵신 개념들을 이해합니다.
- Spring Framework Core 모듈을 이해합니다.
- Spring Framework JDBC 모듈을 이해합니다.
- Spring Framework 웹 애플리케이션을 이해합니다. (MVC)
- Spring Boot로 스프링 웹 어플리케이션을 개발 한다.
=> 스프링 부트로 스프링 웹 어플리케이션을 만드는 것이다.
- 1주차는 개념적인 것을 위주로 살펴볼 것이다.
1. 프로젝트 생성 및 환경 설정
Build Tool 선택하기
- Maven
- Gradle
Build?
- 어플리케이션을 구성한다고 이야기하기도 한다.
- 소프트웨어 프로젝트를 빌드 한다고 한다.
- 필요한 라이브러리를 다운 받고 classpath에 추가합니다.
- 소스 코드를 컴파일 합니다.
- 테스트를 실행합니다.
- 컴파일된 코드를 packaing합니다. → jar / war / zip etc
- packing된 파일을 주로 artifacts 라고 부르고 서버나 레파지토리에 배포합니다.
- 이런 task들을 자동화하게 해준다.
- task 들을 기술한 것을 build script라고 한다.
Maven
: 빌드 도구로써 주로 Java 기반의 프로젝트에서 많이 사용
XML기반으로 설정 모델을 제공하고 pom.xml 파일로 작성
POM : Project Object Model
- pom.xml 파일을 보면 프로젝트 구조를 파악할 수 있다.
- 어떤 라이브러리를 사용하나?
- 어떤 빌드 작업들이 있나?
Maven 홈페이지를 보면
- Maven이 왜 필요한지
- Maven이 무슨 목표를 갖고 만들어졌는지
알 수 있다.
https://maven.apache.org/what-is-maven.html
Maven – Introduction
Introduction <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you
maven.apache.org
왜 Maven을 사용할까?
- Maven은 archetypes 라는 프로젝트 템플릿을 제공
- 반복 설정을 안 할 수 있음
- SI 회사에서는 프로젝트를 여러 개 한다. 거의 비슷비슷 구조가 비슷한다면 - 프로젝트에서 사용하는 외부 라이브러리인 dependency 를 관리
- 다운로드, 호환성, 최신 버전 - 플러인과 외부 라이브러리를 분리하여 관리
- dependency를 다운닫는 Repository가 로컬이 될 수도 있고
Maven Central 와 같은 공개된 Repository가 될 수도 있음
- 비공개 Repository를 만들 수도 있다.
2. 인텔리제이로 Maven 프로젝트 생성하기
새로운 프로젝트를 생성했다.
- maven-archetype-quickstart
웹 문서를 html으로 작성을 하면 브라우저가 읽어서 DOM을 만든다.
pom -> object model이라는 객체를 만든다.
- 프로젝트 구성이 된다.
- xml로 적어두면 maven이 알아서 구성한다.
- maven은 멀티 모듈도 지원해준다.
- 인텔리제이에서 코드를 변화시키면 오른쪽 상단의 load maven changes를 눌러서 load해줘야 된다.
- Lifecycle
- clean : 빌드가 되어 만들어진 target들을 지운다.
- validate :
- package : 패키지 명령이 실행된다. jar 파일이 생긴다.
- install : local repo에 jar copy
- deploy : public 배포, 서버에 배포
이건 공식문서를 찾아봐도 될 것 같다.
https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
Maven – Introduction to the Build Lifecycle
The build lifecycle is simple enough to use, but when you are constructing a Maven build for a project, how do you go about assigning tasks to each of those build phases? Packaging The first, and most common way, is to set the packaging for your project vi
maven.apache.org
Transitive Dependencies
의존성의 의존성을 이야기한다.
- 특정한 아키텍트 모듈이 다른 의존성을 가지고 있다.
- maven이 이걸 알아서 처리한다.
자세한 내용은 공식 문서에서 읽어보자.
Maven – Introduction to the Dependency Mechanism
provided This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and rela
maven.apache.org
- 트리를 구성해서 가장 원하는 버전을 쓰게끔 한다.
- 의존성이 충돌나지 않게 잘 처리를 한다.
mvn dependency:tree
플러그인을 쓸 수 있게 다운로드된다.
- 의존성의 범위를 지정할 수 있다.
- scope -> runtime/test/compile(기본)/system
- maven을 이용하면 손쉽게 자바 프로젝트를 구성할 수 있다.
Gradle
: Build Tool로서 Groovy 기반으로 빌드 스크립트
- 최근에는 코틀린도 지원, 코틀린으로 Build Script 작성 가능
- xml 보다 간결하게 Build Script 작성 가능
- 하나의 프로젝트는 하나 이상의 task 로 구성된다.
- 일반적으로 Gradle 에서는 task를 플러그인에 의해서 제공됨
Project & Task
- Gradle Build는 하나 이상의 프로젝트를 지원
- Maven의 Multiple Module와 비슷 - Task : 클래스를 컴파일하거나 Jar를 생성하거나 하는 build를 위해 하는 작업
Plugin
- Gradle에서 실제 Task와 주요한 기능들을 추가하게 하는 것
- 하나의 프로젝트는 여러 개의 플러그인을 추가할 수 있다.
- 플러그인을 추가하게 되면 새로운 Task들이 추가되고 도메인 객체나 특정 컨벤션이 추가된다.
https://docs.gradle.org/current/userguide/getting_started.html
Getting Started
Everyone has to start somewhere and if you’re new to Gradle, this is where to begin.
docs.gradle.org
요런 컨벤션을 가지고 만들어지게 된다.
- 공식문서를 보면 플러그인을 쓸 수 있는 방법을 알 수 있다.
Maven vs Gradle
- 처음이라면 Gradle 추천
- 이번 실습에서는 Maven을 사용할 것이다.
maven은 xml이다 보니까 설정을 바꿀 필요가 없다. 더 쉽게 쓸 수 있다.
* 요구사항
- maven과 gradle의 차이점과 구조 이해
밑에 두 사이트를 참고해보자.
https://gradle.org/maven-vs-gradle/
Gradle | Gradle vs Maven Comparison
High-level performance and feature comparison between Gradle and Maven
gradle.org
https://www.jrebel.com/blog/java-build-tools-comparison
Ant vs Maven vs Gradle: Java Build Tools Comparison | JRebel by Perforce
Dive into Gradle vs Maven vs Ant as we explore the most popular Java build tools. Check out our rating of each one and see which is best for your project.
www.jrebel.com
Spring Intializer
Spring 기반의 어플리케이션 프로젝트 만들기
프로젝트 만들 때 다양한 방법이 있다.
스프링부트가 나오기 전까지는 하나하나 설정을 해줘야 했다.
- Spring Boot를 통해서 Spring 어플리케이션을 만들 수 있다.
강사님이 MAC 버전으로 스프링부트 시작하는 것을 알려주셨다.
나는 윈도우라 공식 문서를 좀 더 읽어보고, 시작해봐야겠다.
스프링 역사
스프링 framework 시작하기 첫 시간이다.
* 학습 목표
- Spring
- Spring Framework
- Spring Boot
Spring Framework의 시작
- 2002년도 Rod Johnson이 발행한 Expert One-to-One: J2EE Design and Development
<책의 주요 테마>
- Simplicity
- Productivity
- The fundamental importance of object orientation
- The primacy of business requirement
- The importance of testability
- EJB의 겨울에서 봄이 온다는 뜻으로 Spring이라고 지었다.
Spring Projects
- 예전에는 Spring == Spring Framework였다.
- Spring은 여러 프로젝트를 구성되어 있는 자바 기반의 프로그래밍
- 방대한 기능을 제공하는 framework다.
- 여러 프로젝트의 묶음이다.
- 왜 스프링이 사용되어야 하는가? (아래의 사이트를 참고하자.)
https://spring.io/why-spring/
Spring | Home
Cloud Your code, any cloud—we’ve got you covered. Connect and scale your services, whatever your platform.
spring.io
- 실제로 웹 어플리케이션을 개발할 때에는 여러 스프링 관련 프로젝트를 함께 사용한다.
- Spring Boot, Spring Cloud, String Batch, Spring shell, Spring HATEOAS 등등
Spring Framework
: 주요 특징은 공식문서를 보면 확인할 수 있다.
Spring은 여러 프로젝트로, Spring Framework는 여러 모듈로 이뤄져 있다.
위의 feature와 관련된 모듈이 존재한다.
Spring Boot
: 스프링 부트는 '컴퓨터를 부팅한다'는 말처럼 시스템을 사용 가능한 상태로 만드는 것이다.
아이콘도 컴퓨터의 전원 버튼 모양이다.
- Spring Application을 통한 손쉬운 실행
- Auto Configuration
- 쉬운 외부 환경 설정 - Properties, YAML, Command line 설정 등
- 프로파일을 통한 실행환경 관리
- Packaging Executable Jar
- Developer Tools
- 김송아 강사님께서 스프링부트는 밀키트라고 하셨는데 그 말이 도움이 많이 된다.
의존성(1)
Spring Framework의 핵심 개념을 살펴보자.
아래의 공식문서에서 자세한 내용을 볼 수 있다.
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
Core Technologies
In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do
docs.spring.io
스프링 기초 과정을 진행하면서, 주문 관리 어플리케이션을 만들어 볼 것이다.
예제 코드를 작성하면서 몇 가지 용어가 등장하게 된다.
이 용어들은 DDD에서 주로 나오는 용어들이다.
Domain Driven Design
- Domain
: 사용자가 어플리케이션을 사용하는 대상 영역
- 비즈니스 자체가 도메인이 된다.
- 예 ) 주문관리 어플리케이션은 주문관리 자체가 도메인이 된다.
- 복잡성을 해결하기 위해서 모델을 만든다.
-> 이것을 다이어그램으로 표현하고 설계한다. - Entity
: 다른 엔티티와 구분할 수 있는 식별자를 가지고 있다.
- 시간의 흐름에 따라서 지속적으로 변경된다. (상태)
- 독립적, unique
- 모든 모델을 클래스로 작성, 객체가 있다.
=> 객체 == 엔티티, 엔티티는 식별자가 꼭 있어야 한다.
- 식별자는 고유해서 도메인에서 개별성이 있어야 한다.
- 예 ) 주문관리 어플리케이션에서 '주문'이 엔티티다. + 고객 - Value Object
: 값 객체
- 값 속성이 개별적으로 변하지 않고, 자체로 고유한 객체다.
- 한번 만들어지면 변하지 않는다.
- 예 ) 100원 동전 : 값이 변하지 않는다.
- 엔티티들이 value object를 속성으로 가지고 있다.
- 예 ) 주문관리 어플리케이션 : 주문에 대한 주문자는 바뀌지 않는다.- Entity, Value Object, Aggregate : State and Behavier
- Data Transfer Object : State only
- Service, Repository : Behavior only
스프링 프레임워크를 이해하기 위한 예제 코드를 작성해보자.
// Order.java
public class Order{
private final UUID orderid; // 식별자를 uuid로 많이 만든다.
private final UUID costomerId;
private final List<OrderItem> OrderItems;
private long discountAmount;
private OrderStatus orderStatus = OrderStatus.ACCEPTED;
public Order(UUID orderid, UUID customerId, long discountAmount, OrderStatus orderStatus) {
this.orderid = orderid;
this.customerId = customerId;
this.discountAmount = discountAmount;
this.orderStatus = orderStatus;
}
public long totalAmount() {
var beforeDiscount = orderItems.stream().map(v -> v.getProductPrice() * v.getQuentity())
.reduce(0L, Long::sum);
return beforeDiscount - discountAmount;
}
public void setDiscountAmount(long discountAmount) {
this.discountAmount = discountAmount;
}
public void setOrderStatus(OrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
}
// OrderItem.java
public class OrderItem {
public final UUID productId;
public final long productPrice;
public final long quantity;
public OrderItem(UUID productId, long productPrice, int quantity) {
this.productId = productId;
this.productPrice = productPrice;
this.quantity = quantity;
}
public UUID getProductId() {
return productId;
}
public long getProductPrice() {
return productPrice;
}
public Long getQuantity() {
return quantity;
}
}
Java 14 부터 record를 지원한다.
OrderItem을 record 형으로 바꿔보자.
// OrderItem.java
import java.util.UUID;
public record OrderItem(UUID productId, long productPrice, int quantity){
// 알아서 불변개체로 만들어준다.
}
- VO는 불변이어야 한다.
아래의 사이트를 참고해보자.
https://www.baeldung.com/java-record-keyword
// OrderStatus.java
public enum OrderStatus { // 다양한 형태의 상태가 존자한다.
ACCECPTED,
PAYMENT_REQUIRED,
PAYMENT_CONFIRMED,
PAYMENT_REJECTED,
READY_FOR_DELIVERY,
SHIPPED,
SETTLED,
CANCELLED
}
의존성 관리
- 자바에서는 객체를 만들고, 객체 간 협력하도록 코드를 짠다.
- 스프링의 주요 기능 중 하나가 의존성 관리를 해주는 것이다.
- 어떤 객체가 협력하기 위해서 다른 객체가 필요할 때 두 객체 사이에 의존성이 존재하게 된다.
- 의존성은 실행 시점과 구현 시점에 서로 다른 의미를 가진다.
- 컴파일타임 의존성 : 코드를 작성하는 시점에서 발생하는 의존성, 클래스 사이의 의존성
- 런타임 의존성 : 에플리케이션이 실행되는 시점의 의존성, 객체 사이의 의존성
- order가 discountAmount 대신 fixedAmountVoucher를 갖도록 하자.
// Order.java
public class Order{
private final UUID orderid; // 식별자를 uuid로 많이 만든다.
private final UUID costomerId;
private final List<OrderItem> OrderItems;
private FixedAmountVoucher fixedAmountVoucher;
private OrderStatus orderStatus = OrderStatus.ACCEPTED;
public Order(UUID orderid, UUID customerId, long discountAmount, OrderStatus orderStatus) {
this.orderid = orderid;
this.customerId = customerId;
this.discountAmount = discountAmount;
this.orderStatus = orderStatus;
this.fixedAmountVoucher = new FixedAmountVoucher(discountAmount);
}
public long totalAmount() {
var beforeDiscount = orderItems.stream().map(v -> v.getProductPrice() * v.getQuentity())
.reduce(0L, Long::sum);
// Voucher 에게 계산해달라고 위임할 것이다.
return fixedAmountVoucher.discount(beforeDiscount);
}
public void setOrderStatus(OrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
}
public class FixedAmountVoucher {
private final long amount;
public FixedAmountVoucher(long amount) {
this.amount = amount;
}
// 바우처가 주어진 금액에 대하여 어떻게 discount 할 지
public long discount(long beforeDiscount) {
return beforeDiscount - amount;
}
}
main class
// OrderTester.java
import java.util.UUID;
public class OrderTester {
public static void main(String[] args){
var customerId = UUID.randomUUID();
var orderItems = new ArrayList<OrderItem>() {{
add(new OrderItem(UUID.randomUUID(), 100L, 1));
}};
// 10원 세일을 받는다.
var order = new Order(UUID.randomUUID(), customerId, orderItems, 10L);
Assert.isTrue(order.totalAmount() == 90L, MessageFormat("totalAmount {0} is not 90L", order.totoalAmount())));
}
}
이미 코드를 짜면서 discount 될 것이란 것을 알고 있다.
Fixed를 쓰면서 하드코딩 되었다고 볼 수 있다.
- 의존성 : Fixed.. 코드를 고치면 test에서 실패하게 된다.
- 바람직한 의존성이란 설계를 재사용하기 쉽게 만드는 것을 의미한다.
런타임 의존성을 가지게 해야 한다.
discount 변경이 용이하게 바뀌어야 한다.
- Voucher라는 인터페이스를 도입하자.
// Voucher.java
// 바우처도 하나의 Entity다.
public interface Voucher {
UUID getVoucherId();
long discount(long beforeDiscount);
}
public class FixedAmountVoucher implements Voucher {
private final UUID voucherId;
private final long amount;
public FixedAmountVoucher(UUID voucherId, long amount) {
this.voucherId = voucherId;
this.amount = amount;
}
@Override
public UUID getVoucherId() {
return voucherId;
}
public long discount(long beforeDiscount) {
return beforeDicount - amount;
}
}
public class PercentDiscountVoucher implements Voucher {
private final UUID voucherId;
private final long percent;
@Override
public UUID getVoucherId() {
return voucherId;
}
public long discount(long beforeDiscount) {
return beforeDicount * (percent / amount);
}
}
// Order.java
public class Order{
private final UUID orderid; // 식별자를 uuid로 많이 만든다.
private final UUID costomerId;
private final List<OrderItem> orderItems;
private Voucher voucher;
private OrderStatus orderStatus = OrderStatus.ACCEPTED;
public Order(UUID orderid, UUID customerId, List<OrderItem> orderItems, Voucher voucher) {
this.orderid = orderid;
this.customerId = customerId;
this.orderItems = orderItems;
this.voucher = voucher;
}
public long totalAmount() {
var beforeDiscount = orderItems.stream().map(v -> v.getProductPrice() * v.getQuentity())
.reduce(0L, Long::sum);
// Voucher 에게 계산해달라고 위임할 것이다.
return fixedAmountVoucher.discount(beforeDiscount);
}
public void setOrderStatus(OrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
}
'2023 활동 - 4학년 > [1월 ~ 4월] sw 아카데미 백엔드 과정' 카테고리의 다른 글
[2023.02.20 / CNU SW 아카데미] 34일차 회고록 (0) | 2023.02.20 |
---|---|
[2023.02.19 / CNU SW 아카데미] Maven과 Gradle의 차이점 (0) | 2023.02.19 |
[2023.02.16 / CNU SW 아카데미] 32일차 회고록 (0) | 2023.02.17 |
[2023.02.15 / CNU SW 아카데미] 31일차 회고록 (0) | 2023.02.17 |
[2023.02.17 / CNU SW 아카데미] 33일차 회고록 (0) | 2023.02.17 |