이 글은 스프링 핵심 원리 - 기본편을 기반으로 작성되었습니다.
객체 지향 원리 적용
애자일
애자일(Agile)은 소프트웨어 개발을 비롯한 다양한 프로젝트 관리 방식에서 사용되는 유연하고 반복적인 접근 방식
프로젝트를 진행할 경우에 생각보다 많이 쓸 일이 있으니 잘 알아두는 편이 좋다.
내가 이해한 대로 간단히 요약하면 애자일은 "소통" 이라는 단어 하나로 말할 수 있을 것 같다.
고객과 소통, 팀원과 소통, 프로젝트와의 소통을 통하여 피드백을 적극적으로 수용하고 유연하게 대처하는 것이다.
회의.. 회의... 많은 회의와 준비..
GPT가 알려주는 애자일
전통적인 개발 방법론인 워터폴(Waterfall) 방식이 각 단계를 순차적으로 진행하는 것과 달리, 애자일은 **짧은 주기(스프린트)**로 작업을 나누어 개발 과정 중 지속적으로 고객 피드백을 반영하고, 요구사항의 변화에 빠르게 대응할 수 있도록 설계되었습니다.
---
**애자일의 핵심 원칙**
애자일은 2001년 발표된 **애자일 선언(Agile Manifesto)**에서 4가지 핵심 가치와 12가지 원칙으로 정의됩니다.
**4가지 핵심 가치**
1. **개인과 상호작용** > 프로세스와 도구
- 소통과 협력을 통해 문제를 해결하는 데 집중.
2. **작동하는 소프트웨어** > 방대한 문서
- 실제로 작동하는 제품을 만드는 데 우선순위.
3. **고객 협업** > 계약 협상
- 고객과 긴밀히 협력하여 요구사항 변화에 유연하게 대응.
4. **변화에 대한 대응** > 계획 준수
- 초기 계획에 얽매이지 않고, 변화에 빠르게 적응.
**12가지 원칙**
1. 고객 만족을 위해 지속적으로 가치 있는 소프트웨어 제공.
2. 요구사항 변화는 언제든지 수용.
3. 짧은 주기(1~4주)로 작동 가능한 소프트웨어 제공.
4. 비즈니스와 개발팀 간의 협력.
5. 동기 부여된 팀원 중심으로 프로젝트 구성.
6. 대면 소통을 최우선.
7. 작동하는 소프트웨어를 진척의 주요 척도로 삼음.
8. 지속 가능한 개발.
9. 기술적 탁월성과 설계 간의 균형 유지.
10. 단순함을 추구.
11. 자율적인 팀이 최고의 아키텍처와 설계를 창출.
12. 팀은 정기적으로 성과를 돌아보고 개선.
---
**애자일 방법론의 장점**
1. **유연성**: 변화하는 요구사항에 빠르게 적응.
2. **고객 중심**: 고객의 지속적인 피드백을 반영.
3. **리스크 감소**: 짧은 주기로 제품을 점진적으로 완성.
4. **팀워크 강화**: 개발자, 디자이너, 고객 간 협력이 핵심.
5. **품질 향상**: 지속적 테스트와 반복적 개선.
---
**애자일 방법론의 구현 사례**
- **스크럼(Scrum)**: 스프린트와 데일리 스탠드업 미팅 등을 통해 팀의 업무를 관리.
- **칸반(Kanban)**: 작업 흐름을 시각화하고 WIP(진행 중인 작업)를 제한.
- **XP(eXtreme Programming)**: 짧은 주기의 개발과 테스트 주도 개발(TDD) 중심.
---
**애자일이 필요한 이유**
전통적인 방법론이 고정된 요구사항과 긴 개발 주기를 전제로 한다면, 애자일은 **변화와 불확실성**이 많은 환경에 적합합니다. 현대 소프트웨어 개발에서 요구사항은 계속 변화하며, 이를 적시에 반영할 수 있어야 하기 때문에 애자일은 특히 IT 업계에서 널리 사용됩니다.
새로운 할인 정책 개발
새로운 할인 정책이 생겼다... 이미 개발 다 했는데....
요구사항은 다음과 같다.
! 고정 할인이 아닌 정률(%) 할인 정책으로 변경 !
허허 실무에서는 이런 일이 많이 발생하려나...
하지만 계발 단계 부터 객체지향을 따라 개발했기에 변화에 대응하기 쉽다!!
이전 인터페이스에 구현체를 하나 추가해 주면 된다.
package hello.demo.discount;
import hello.demo.member.Grade;
import hello.demo.member.Member;
public class RateDiscountPolicy implements DiscountPolicy {
private int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP){
return price * discountPercent/100;
}else {
return 0;
}
}
}
새로운 할인 정책 적용하기
적용을 위해서는 OrderServiceImpl을 수정해야한다.
구현체를 아래와 같이 수정해야하는데
FixDiscountPolicy -> RateDiscountPolicy
이는 무언가 이상하다. 분명 설계는 인터페이스만 의존하도록 했지만
실제 구현에서는 인터페이스와 구현체 모두를 바라보는 관계가 형성된 것이다.
이건 DIP 위반이다.
그럼 어떻게 해야할까...?
바로 의존성 주입이 여기서 사용된다.
관심사 분리
하나의 역할은 하나의 책임을 가져야 한다.
하지만 위 코드는 Order가 주문과 지정을 동시에 감당하고 있다.
그렇다면 어떻게 해야할까? 역할을 하나 추가하면 되지 않을까??
지정을 담당하는 클래스(AppConfig)를 만들어 프로젝트의 설정을 관리하는 역할을 맡기자.
의존성 주입 - 생성자 주입
AppConfig 리팩터링
AppConfig는 설정 파일이고 의존성을 담고 있기에 전체적인 그림이 보여야한다.
아래와 같은 구조가 보이면 좋다.
기존은 조금 허접한 느낌이니 변경해주자.
변경한 구조
package hello.demo.order;
import hello.demo.discount.DiscountPolicy;
import hello.demo.discount.FixDiscountPolicy;
import hello.demo.member.MemberRepository;
import hello.demo.member.MemberService;
import hello.demo.member.MemberServiceImpl;
import hello.demo.member.MemoryMemberRepository;
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
private MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(),discountPolicy());
}
public DiscountPolicy discountPolicy(){
return new FixDiscountPolicy();
}
}
이전 구조와 달리 중복이 제거되었고 전체적인 구성이 한눈에 들어온다.
새로운 구조와 할인 정책 적용
이전에 하려다 하지 못했던 작업을 시행해보자.
구현체를 아래와 같이 수정해야하는데
FixDiscountPolicy -> RateDiscountPolicy
우린 AppConfig가 있기 때문에 다른 것을 수정할 필요가 없다.
discountPolicy 의 구현체만 수정하면 정책이 변경된다.
천원만 할인되던 금액이 10% 할인으로 변경되었다.
spring으로 전환하기
지금까지 개발을 초기 세팅이외에 순수한 자바만을 사용하여 개발을 진행하였다.
이제 spring을 적용하여보자...!
어노테이션을 통해 스프링 컨테이너를 등록하고 컨테이너에서 찾아서 사용하자.
스프링 컨테이너와 스프링 빈
컨테이너 생성
아래의 코드를 통해 컨테이너를 생성하고 AppConfig.class를 활용하여 구성정보를 지정해 준다.
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
컨테이너에 등록된 모든 빈 조회
필요한 정보들을 얻기위한 조회 작업을 익혀보자.
package beanfind;
import hello.demo.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
String[] beanNames = ac.getBeanDefinitionNames();
for(String beanName : beanNames){
Object bean = ac.getBean(beanName);
System.out.println(bean);
}
}
@Test
@DisplayName("어플리케이션 빈 출력하기")
void findApplicationBean(){
String[] beanNames = ac.getBeanDefinitionNames();
for(String beanName : beanNames){
BeanDefinition def = ac.getBeanDefinition(beanName);
if (def.getRole() == BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanName);
System.out.println(bean);
}
}
}
}
상속 관계 조회
부모 타입 조회시에 자식타입도 함께 조회된다.
- 부모타입 조회시 둘 이상의 자녀가 있으면 중복오류
- 부모타입 조회시 둘 이상의 자녀가 있으면 빈 이름지정
- 특정 하위 타입으로 조회 ( 추천하지 않음 )
- 부모타입으로 모두 조회
- Object 로 조회 ( 가장 상위 객체로 조회 )
++ print 하는건 강의영상이고 공부하는 것이니 사용! 시스템이 결정할 수 있도록 test에서는 되도록 쓰지않는 것이 좋다..
BeanFactory와 ApplicationContext
- BeanFactory: 스프링의 최상위 컨테이너로, 객체(빈)를 생성하고 관리하며, 지연 로딩 방식으로 필요한 시점에 빈을 초기화
- ApplicationContext: BeanFactory를 확장한 컨테이너로, 라이프사이클 관리, 국제화, 이벤트 처리 등 더 많은 기능을 제공하며, 일반적으로 애플리케이션 개발에 주로 사용
자바코드,XML
설정파일을 xml로 사용할 수도 있다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="memberService" class="hello.demo.member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository" />
</bean>
<bean id="memberRepository"
class="hello.demo.member.MemoryMemberRepository" />
<bean id="orderService" class="hello.demo.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository" />
<constructor-arg name="discountPolicy" ref="discountPolicy" />
</bean>
<bean id="discountPolicy" class="hello.demo.discount.RateDiscountPolicy" />
</beans>
이게 좋은가..?
잘 모르겠으니 GPT에게 물어보자
되게 단호하게 요새는 이런거 안쓴다고 말해주는 것 같으니 이런게 있구나 정도로만 넘어가자.
스프링 빈 설정 메타 정보
스프링 컨테이너는 BeanDefinition을 바라보고 있다.
그럼 이 파일은 어떻게 설정들을 가져오는 것일까
config 파일에서 reader 로 넘기고 다 읽고나서 메타정보를 생성한다.
이 메타정보들이 설정으로 관리되는 것들이라고 생각하면 된다.
이 자체를 너무 깊게 이해하기 보단 위 그림의 구조를 기억하는 것이 향후 도움이 될 것이다.
빈 설정 메타데이터에 들어있는 내용들
- 빈 이름
- 빈을 식별하기 위한 고유한 이름 또는 별칭.
- 예: <bean id="myBean" ...> (XML), @Bean(name = "myBean") (Java Config).
- 빈 클래스
- 해당 빈을 생성하기 위해 사용되는 클래스.
- 예: <bean class="com.example.MyClass" ...>.
- 스코프(Scope)
- 빈의 라이프사이클을 정의.
- 예: singleton, prototype, request, session, application.
- 의존성 주입(DI) 정보
- 빈이 생성될 때 필요한 의존성을 정의.
- XML: <property> 태그.
- Java Config: 메서드 파라미터나 @Autowired.
- 초기화 및 소멸 메서드
- 빈이 생성된 후 호출될 초기화 메서드와 소멸 전 호출될 메서드.
- 예: <bean init-method="init" destroy-method="cleanup">.
- 빈의 역할(Role)
- ROLE_APPLICATION: 애플리케이션 로직과 관련된 빈.
- ROLE_INFRASTRUCTURE: 스프링 내부에서 사용하는 빈.
- 프로퍼티 및 값 설정
- 빈 생성 시 초기화할 값이나 프로퍼티를 정의.
- 예: <property name="key" value="value" />.
- 조건 및 프로파일
- 특정 조건이나 환경(Profile)에 따라 빈이 로드될지 여부를 설정.
- 예: @Profile("dev").
- 팩토리 메서드
- 정적 또는 인스턴스 팩토리 메서드를 통해 빈을 생성하도록 설정.
- 예: <bean factory-method="createInstance" />
후기..
와 생각보다 삶이 빠듯해서 목표였던 프로젝트의 서버는 아직 건들지도 못했다..
아 군대 가야하는데.. 언제 또 하려나
그래도 이제 스프링이 뭔지 대충 어떻게 개발하는지가 익혀지는 것 같다.
아직 설명은 못하겠지만
착실히 하다보면 더욱 깊게 이해될 것이라 믿는다.
'WINK-(Web & App) > Spring Boot 스터디' 카테고리의 다른 글
[2024-2 Spring Boot 스터디] 김문기 #4주 (1) | 2024.11.21 |
---|---|
[2024-2 SpringBoot 스터디] 탁태현 #4주차 (0) | 2024.11.21 |
[2024-2 SprintBoot 스터디] 김아리 #4주차 (1) | 2024.11.20 |
[2024-2 SpringBoot 스터디] 정호용 #4주차 섹션 4~5 (0) | 2024.11.19 |
[2024-2 SpringBoot 스터디] 정호용 #3주차 섹션 1~3 (0) | 2024.11.17 |