멋쟁이사자처럼_부트캠프/Spring

[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 30일차 Spring

모건_Morgan 2025. 1. 16. 17:44

 

https://spring.io/

 

Spring | Home

Cloud Your code, any cloud—we’ve got you covered. Connect and scale your services, whatever your platform.

spring.io

스프링 공식 홈페이지

 

https://start.spring.io/

다음과 같이 설정하고 Generate 해주었다.


Jar War 차이.


JAR 파일은 Java 클래스 파일, 관련 리소스(이미지, 설정 파일 등) 및 메타데이터를 하나의 파일로 압축한 것.
WAR 파일은 웹 애플리케이션을 구성하는 요소들(서블릿, JSP, HTML, CSS, JavaScript, 이미지, 설정 파일 등)을 하나의 파일로 압축한 것.

 

JAR는 Java 코드와 리소스를 묶어서 배포하는 데 사용되는 반면, WAR는 웹 애플리케이션을 웹 서버/웹 애플리케이션 서버에 배포하기 위해 사용됩니다. 스프링 부트를 사용하면 JAR 파일로도 웹 애플리케이션을 실행할 수 있지만, 전통적인 웹 애플리케이션 배포 방식에서는 WAR 파일을 사용합니다.


설정에서 Build and Run 설정을 위와같이 변경해주었다.

 

스프링부트 이전에는 메이븐 코드를 수동으로 불러왔었다.

https://mvnrepository.com/artifact/org.springframework/spring-core

이제 자동으로 불러온다

 

https://es2sun.tistory.com/246

 

[IntelliJ] 캐시 삭제 및 IDE 재시작

IntelliJ 에서 라이브러리가 제대로 imort되지 않거나 변경사항이 적용되지 않을때 캐시를 지워주면 해결되는 경우가 있다. IntelliJ에서 캐시를 삭제하는 방법은 간단하다. 1. 캐시를 삭제에 관련한

es2sun.tistory.com

프로젝트를 실행할때 오류발생 시 참고하면 좋은 사이트.

 

프로젝트 실행시 build.gradle로 열어준다.

 

build.gradle - dependencies에 아래 내용을 추가 : 서버를 재시작하지않아도 업데이트해줌.

developmentOnly 'org.springframework.boot:spring-boot-devtools'

 

안먹히면 아래 링크를 참조

https://welivememories.tistory.com/11

 

인텔리제이 스프링부트 데브툴즈 안먹힐때(SpringBoot DevTools not working)

1. 쓰게 된 이유 : 항상 STS 사용하다가 인텔리제이로 옮겨서 프로젝트를 할려고 했는데 DevTools가 안먹혀서 쓰게 됐다. 2. 시도해본 내용: 구글 검색해서 기존에 나와있는 방법은 다 해봤다. - 1. Comp

welivememories.tistory.com

 

application.properties의 확장명을 .yml로 바꿔주고

내용을 바꿔주면 톰캣서버가 포트번호 8888(입력한값)으로 바뀌는것을 확인할 수 있다.

properties는 오타(오류)를 체크해주지않지만 yml은 오타를 체크해준다.

 

Spring Boot는 애플리케이션 시작 시 콘솔에 배너를 출력하는 기능을 제공한다. 기본적으로 Spring 로고가 출력되지만, src/main/resources 폴더에 banner.txt 파일을 추가하면 사용자 정의 텍스트를 배너로 출력할 수 있다.

리소스 폴더에 banner.txt 파일을 생성하고 내용을 입력하면 애플리케이션 시작 시 해당 텍스트가 콘솔에 출력되도록 설정이 가능하다.


1. 프레임워크란 무엇인가?

  • 프레임워크란 무엇인가?
    • 웹 애플리케이션 개발에 필요한 기본 구조와 규칙을 제공하는 골격.
    • 규칙과 표준 제공: 일관된 구조로 협업 및 유지보수가 용이함.
    • 개발자가 특정 규칙에 따라 코드를 작성해야 하며, 프레임워크가 애플리케이션의 흐름을 제어함(IoC).

  • 프레임워크와 라이브러리의 차이는 무엇인가?
    • 프레임워크는 전체 애플리케이션의 틀과 흐름을 제공하며, 개발자는 이 틀에 맞춰 작업합니다. 프레임워크의 규칙을 어기거나 원하는대로 기능을 추가하는것은 어렵습니다.
    • 라이브러리는 특정 기능을 구현하기 위한 도구로, 개발자가 호출하여 사용합니다. 프레임워크와 같은 정해진 규칙은 없습니다.
  • 우리가 왜 스프링 같은 프레임워크를 사용해야 할까?
    • 객체 의존성을 명시적으로 관리하여 결합도를 낮추고 테스트 용이성을 높입니다.
    • 반복적인 작업(객체 생성 및 의존성 주입(DI) 등)을 자동화하여 개발 생산성을 높입니다.

2. IoC (Inversion of Control, 제어의 역전)

 

IoC란 무엇인가?

  • IoC는 제어의 역전이라는 의미로, 프로그램의 제어 흐름(메소드나 객체의 호출 작업 등)을 개발자가 아닌 프레임워크가 관리하는 디자인 원칙을 의미함
  • 기존의 경우:
  • 객체 생성 → 의존성 객체 생성 → 의존성 객체 메소드 호출
  • 스프링:
  • 객체 생성 → 의존성 객체 주입 (제어권을 스프링에게 위임하여 스프링이 만들어 놓은 객체를 주입한다) → 의존성 객체 메소드 호출

 

IoC가 없을 때 발생하는 문제는 무엇인가?

1. 높은 결합도

  • 객체가 직접 의존하는 객체를 생성하고 관리하므로 결합도가 높아짐

  IoC가 없는 경우 예시,

public class OrderService {
    private PaymentService paymentService;

    public OrderService() {
        this.paymentService = new PaymentService(); // 직접 생성
    }

    public void processOrder(Order order) {
        // 주문 처리 로직
        paymentService.processPayment(order.getPaymentInfo()); // 결제 처리
        // ...
    }
}

public class PaymentService {
    public void processPayment(PaymentInfo paymentInfo) {
        // 결제 처리 로직
        // ...
    }
}

  주문 서비스(OrderService)는 결제 서비스(PaymentService)를 직접 생성하고 사용해야 함. 이는 다음과 같은 문제점을 야기합니다.

 

  • PaymentService 변경 시 OrderService 수정 필요: 만약 PaymentService의 생성자 인자가 변경되거나, 다른 결제 방식으로 변경해야 하는 경우, OrderService의 코드도 함께 수정해야 합니다. 예를 들어, PaymentService가 카드 결제 외에 계좌 이체 기능도 지원하도록 변경되어 생성자에 추가 인자가 필요하게 되면, OrderService의 생성자도 함께 수정해야 합니다.
  • 테스트의 어려움: OrderService를 테스트하기 위해서는 PaymentService가 필요합니다. 단위 테스트를 수행하기 어렵고, PaymentService에 문제가 발생하면 OrderService의 테스트도 영향을 받습니다. 가짜 PaymentService 객체(Mock 객체)를 만들어서 테스트하는 것도 번거롭습니다.
  • 코드 재사용의 어려움: OrderService는 특정 PaymentService에 강하게 의존하고 있기 때문에, 다른 환경에서 재사용하기 어렵습니다. 예를 들어, 다른 쇼핑몰 시스템에서 OrderService를 사용하려고 할 때, 해당 시스템의 결제 방식에 맞는 PaymentService를 사용해야 하지만, 기존 코드로는 불가능합니다.

 

  IoC를 사용하는 예시,

@Service
public class OrderService {

    @Autowired
    private PaymentService paymentService; // 의존성 주입

    public void processOrder(Order order) {
        // 주문 처리 로직
        paymentService.processPayment(order.getPaymentInfo()); // 결제 처리
        // ...
    }
}

@Service
public class PaymentService {
    public void processPayment(PaymentInfo paymentInfo) {
        // 결제 처리 로직
        // ...
    }
}

 

IoC 컨테이너(예: Spring 컨테이너)를 사용하면 객체의 생성과 의존성 관리를 컨테이너에 위임할 수 있습니다. 

Spring 컨테이너는 @Autowired 어노테이션을 보고 OrderService에 필요한 PaymentService를 주입해줍니다. 이렇게 하면 OrderService는 PaymentService를 직접 생성할 필요가 없어지고, 결합도가 낮아집니다.

  • PaymentService가 변경되어도 OrderService 코드를 수정할 필요가 없습니다.
  • OrderService를 테스트할 때 Mock 객체를 사용하여 PaymentService를 대체할 수 있습니다.
  • OrderService를 다른 환경에서 재사용하기 용이합니다.

이처럼 IoC를 사용하면 객체 간의 결합도를 낮추어 코드의 유연성, 재사용성, 테스트 용이성을 향상시킬 수 있습니다.

 

2. 유지보수가 어려움

  • 변경 사항이 있을 때 관련된 코드를 전부 수정해야 됨

IoC가 제공하는 장점은 무엇인가?

  1. 유연성 증가
    • 의존성을 외부에서 주입받아 사용하므로 클래스 간 결합도가 낮아짐
  2. 유지 보수 용이
    • 변경 사항이 적은 코드 영역으로 한정돼서 유지보수가 쉽다

3. DI (Dependency Injection, 의존성 주입)

  • DI란 무엇인가?예를 들어, A라는 클래스가 B라는 클래스를 사용해야 한다고 가정하면 보통 A 안에서 B를 new B()로 직접 생성함. 하지만 DI를 사용하면, 누군가(스프링 같은 프레임워크나 다른 구성 부분)가 B 객체를 미리 만들어서 A에게 넘겨줌.
  • DI(의존성 주입)는 객체가 사용할 의존 객체(협력 객체)를 스스로 생성하지 않고, 외부에서 만들어 주입받는 방식을 의미함
  • DI를 사용하는 이유는 무엇인가?
    1. 유연하고 유지보수하기 쉬움
    2. 의존 객체가 바뀌어도, 사용하는 쪽 코드를 크게 고칠 필요가 없음. 즉 인터페이스만 맞춰주면, 다른 구현체로 쉽게 교체할 수 있음.
    3. 결합도(의존도) 감소
    4. 클래스 A가 직접 클래스 B를 생성하는 방식(new B())을 피하므로 A와 B의 강한 연결고리를 끊어줄 수 있음.
    5. 테스트 용이성
    6. 테스트할 때 가짜 객체(목, 스텁)를 주입해서 테스트하기가 쉬워짐. 예를 들어, 실제 DB 대신 임시 DB 객체를 만들어주면 테스트 시간을 획기적으로 줄일 수 있음
  • DI의 구현 방식에는 어떤 것이 있는가?
    • 구현 방법에 따라 세 가지 주요 방식으로 나뉩니다. 각 방식은 특정 상황에 더 적합한 경우가 있으며, 스프링에서는 이 세 가지 방식을 모두 지원합니다.
  • 생성자 주입(Constructor Injection)예시:
@Component
public class UserService {

    private final UserRepository userRepository;

    // 생성자를 통한 의존성 주입
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void doSomething() {
        userRepository.findUser();
    }
}

 

스프링 설정

@Configuration
public class AppConfig {

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

 

@Service
public class OrderService {
private final PaymentService paymentService;
	
// 생성자를 통해서 의존 객체를 주입받음
public OrderService(PaymentService paymentService) {
	   this.paymentService = paymentService;
}
	
// ...
}
장점: final 키워드로 불변 객체가 가능하고, 의존 객체 누락 시 컴파일 타임 오류가 떠서 안전
  1. 세터 주입(Setter Injection)예시:
  2. 생성자 말고 세터(Setter 메서드)로 객체를 주입받음.
@Service
public class OrderService {
private PaymentService paymentService;

@Autowired

// 세터(Setter 메서드)로 객체를 주입받음.
public void setPaymentService(PaymentService paymentService) {
    this.paymentService = paymentService;
}
// ...
}

장점: 필요에 따라 주입을 선택적으로 할 수 있음. 단점: 객체가 생성된 후에 별도 세터를 호출해야 해서 불완전 상태(초기값이 세팅 전)가 될 위험이 있음.


스프링 부트란?

왜 사용했는가?

스프링 부트는 스프링 프레임워크를 기반으로 애플리케이션을 더 빠르고 쉽게 개발하고 배포할 수 있도록 도와주는 도구입니다. 특히, 복잡한 설정을 자동화해주어 개발자가 비즈니스 로직에 집중할 수 있도록 해주며, 내장된 서버를 통해 별도의 서버 설정 없이도 애플리케이션을 실행할 수 있다는 장점이 있습니다. 이러한 특징들 덕분에 개발 생산성을 크게 향상시킬 수 있기 때문에 사용했습니다.

라고 답할 수 있다.

 

개발 속도 및 생산성 향상: "기존 스프링 프로젝트에서 많은 시간을 소모했던 설정 작업들을 자동화해주어 개발 속도를 단축하고 생산성을 향상시킬 수 있습니다."

마이크로서비스 아키텍처에 적합: "마이크로서비스 아키텍처 환경에서 각 서비스를 빠르게 개발하고 배포해야 하기 때문에, 가볍고 빠른 스프링 부트가 적합합니다."

의존성 관리의 용이성: "Starter 종속성들을 통해 필요한 라이브러리들을 쉽게 관리할 수 있어, 의존성 충돌 문제를 줄일 수 있습니다."

운영의 편의성: "애플리케이션의 모니터링, 로깅, 보안 설정 등 운영에 필요한 기능들을 쉽게 사용할 수 있어, 운영 및 관리가 편리합니다."

 

 

소규모 프로젝트: "프로젝트 규모가 작았기 때문에, 복잡한 설정 없이 빠르게 개발할 수 있는 스프링 부트를 선택했습니다. 덕분에 핵심 기능 개발에 집중할 수 있었습니다."

빠른 프로토타이핑이 필요한 프로젝트: "빠르게 프로토타입을 만들어야 하는 프로젝트였기 때문에, 개발 속도가 빠른 스프링 부트를 사용했습니다. 덕분에 빠르게 아이디어를 검증할 수 있었습니다."

기존 스프링 프로젝트에서 마이그레이션: "기존 스프링 프로젝트의 설정을 간소화하고, 최신 스프링 기능을 활용하기 위해 스프링 부트로 마이그레이션했습니다."

 

 

스프링 부트(Spring Boot)는 스프링 프레임워크를 더 쉽고 빠르게 사용할 수 있도록 도와주는 도구.

- 스프링 프레임워크 : 프레임워크는 일종의 반제품, 프레임워크가 내가 만든 것들을 동작시켜준다.

  • 프레임워크 코어(Cold Spot) : 변경되지 않고 반복적으로 재사용 되는 부분이다. 프레임워크에서 제공하는 라이브러리들을 의미하고, 사용하는 자원 관리나 처리 흐름을 제어한다.
  • 확장 포인트(Hook Point) : 애플리케이션을 구축할 때 사용할 확장 포인트를 제공한다. 추상 클래스나 인터페이스 형태로 제공되는 것이 일반적이다.
public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}

이런식으로 추상 클래스나 인터페이스를 선언함.

  • 확장 모듈(Hot Spot) : 각 애플리케이션이 확장 포인트를 상속해서 애플리케이션만의 비즈니스를 구현하는 것을 말한다.
public class LoggingInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 컨트롤러 실행 전 로직 (예: 요청 시간 기록)
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true; // true: 컨트롤러 실행, false: 실행 중지
    }

    // ... (postHandle, afterCompletion 구현)
}

예시 (HandlerInterceptor 구현): HandlerInterceptor 인터페이스를 구현하여 인증/인가, 로깅 등의 기능을 추가할 수 있습니다(개발자가 직접 구현).

  • 메타데이터 : 프레임워크에서 제공하는 Cold Spot Hook Point를 상속해서 구현한 Hot Spot을 유기적으로 결합하여 동작하도록 하는 환경 설정 파일이다. 일반적으로 XML, java Annotation 형태로 작성한다.
@Component
public class MyService {

    @Autowired
    private MyRepository myRepository;

    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }
}

- 어노테이션 형식으로 빈 등록, 의존성 주입, 요청 매핑 등을 설정한 예.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.example"/>

    <bean id="myService" class="com.example.MyService">
        <property name="myRepository" ref="myRepository"/>
    </bean>

</beans>

- XML 기반 설정 : <bean>, <context:component-scan> 등의 XML 태그를 사용하여 빈 등록, 컴포넌트 스캔 등을 설정한 예.

 

 

Spring MVC : Spring Framework에서 웹 애플리케이션을 개발하기 위한 모듈 중 하나