플레이데이터 풀스택 백엔드 9기 10주차 주간회고 및 학습기록 (열번째 기록)
Facts
이번 주는 Servlet과 Spring을 공부했다. 프로젝트 후유증으로 화, 수 컨디션이 좋지 않았는데, 목요일부터 어느정도 회복이 됐다. 3일짜리 프로젝트로 이틀동안 골골댄 건 여유부리다가 막바지에 몰아서 한 탓이다. 최근 공부할 것이 쌓이면서 걱정도 많이 되고 스트레스도 꽤 생겼는데, 이번 주 매니저님과의 상담 시간에 들은 조언이 도움이 되었다. 긴 시간 동안 많은 것을 한꺼번에 배우는 과정이니 너무 다 기억하려고 하기보다는 당장 이해할 수 있는 것만이라도 최대한 흡수하려고 하는 게 좋을 거 같다고 하셨다. 상담을 받고 마음이 한결 가벼워졌다. 그리고 spring이 가장 어려울 거라고 조언해주셨는데 역시 경험자의 조언은 허투루 들으면 안된다. spring 초반부터 어렵다. 선생님께 질문 많이 하면서 수업 들어야겠다...!
Feelings
동기분들 중에 점심시간/쉬는시간에도 끊임없이 모니터를 들여다보고 공부하시는 분이 있는데, 그 분을 볼 때마다 대단하다고 느끼면서도 나도 저렇게 성실하고 싶다고 생각한다. 이해가 잘 안되어도 자꾸 뒤로 미루던 최근 내 모습과 대조적이다. 잘 안되거나 이해가 안되는 게 있으면 곧바로 공부하는 모습이 멋있다. 그 분이 앉아있을 때 나도 앉아서 모니터를 들여다봐야겠다. 이렇게 하면 미루는 습관을 조금이라도 고칠 수 있지 않을까?
Findings
1. 서블릿(객체)과 서블릿 컨테이너
- 서블릿 컨테이너 ➡️ 주방
- 서블릿 클래스 (객체) ➡️ 요리사
- 웹브라우저 ➡️ 손님
- 요청(Request) ➡️ 주문
서블릿 기반 웹 애플리케이션의 요청 처리 흐름
[브라우저]
↓ (HTTP 요청)
[서블릿 컨테이너] ← 예: Tomcat, Jetty
↓
[서블릿 실행] ← 개발자가 만든 클래스 (HttpServlet 상속)
↓
[HTTP 응답 생성]
↓
[브라우저] ← 결과 화면 표시
서블릿 컨테이너가 하는 일
- HTTP 요청을 받아줌 : 클라이언트(브라우저) → 요청 보내면 컨테이너가 받음
- 서블릿 객체 생성 및 실행 : 서블릿 클래스 로딩 → 객체 생성 → service() 메서드 호출
- 생명주기 관리 : init() → service() → destroy() 순서로 서블릿을 관리
- 요청(Request)과 응답(Response) 객체 관리 : HttpServletRequest, HttpServletResponse 객체 생성 및 전달
- 멀티쓰레드 처리 : 여러 사용자의 요청을 동시에 처리 가능 (쓰레드로 분기)
2. 제어의 역전(IoC)와 의존성 주입(DI)
제어의 역전 (IoC, Inversion of Control)
: 프로그램의 제어 흐름 구조가 뒤바뀌는 것
>> 객체의 생성과 관리 , 객체 간의 의존성 처리 등을 프레임워크에서 대신 처리
>> IoC 컨테이너 : 제어의 역전을 구현한 구체적인 프레임워크 (객체의 생성, 초기화, 의존성 처리 등을 자동수행)
Bean
: Spring IoC Container에서 관리되는 객체
>> Spring에서 Bean으로 등록된 객체는 IoC Container에 의해 관리를 받으며, 생성/초기화/의존성 주입/제거 등을 자동으로 처리 가능
의존성 주입 (DI, Dependency Injection)
: 객체 간의 의존 관계를 Bean 설정 정보를 바탕으로 Container가 자동으로 연결
>> 필요한 객체 (의존 객체)를 외부에서 주입받는 설계 방식
>> 객체 간의 결합을 느슨하게 하고 유지보수성 및 유연성 증가
기존 방식
// MemberRepository.java
public class MemberRepository {
public String findMember() {
return "홍창기";
}
}
// =====================================================================================
// OrderService.java
public class OrderService {
private MemberRepository memberRepository = new MemberRepository(); // new를 통한 직접 생성
public String order() {
return "주문자: " + memberRepository.findMember();
}
}
// =====================================================================================
// Main.java
public class Main {
public static void main(String[] args) {
OrderService service = new OrderService(); // new를 통한 직접 생성
System.out.println(service.order());
}
}
- 문제점 1 : OrderService가 MemberRepository의 구체 클래스에 직접 의존
- 문제점 2 : 테스트 시 다른 저장소로 교체 불가
- 문제점 3 : 확장성, 유지보수성, 테스트성 낮음
생성자 기반 의존성 주입
// 인터페이스로 추상화 (Optional)
public interface MemberRepository {
String findMember();
}
// =====================================================================================
// 실제 구현체
public class MemoryMemberRepository implements MemberRepository {
public String findMember() {
return "홍창기";
}
}
// =====================================================================================
// 서비스 클래스
@Service
public class OrderService {
private final MemberRepository memberRepository;
// 생성자 주입
@Autowired // 생략 가능 (생성자 1개일 경우)
public OrderService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public String order() {
return "주문자: " + memberRepository.findMember();
}
}
// =====================================================================================
// Main.java
public class Main {
public static void main(String[] args) {
// 의존성 주입 (직접 또는 스프링이 해줌)
MemberRepository repo = new MemoryMemberRepository();
OrderService service = new OrderService(repo);
System.out.println(service.order());
}
}
| 항목 | 기존 방식 (new를 이용한 직접 생성) | 생성자 주입 (의존성 주입) |
| 의존성 관리 | 서비스 내부에서 new로 생성 | 외부(spring container)에서 주입 |
| 유연성 | 구현체 변경 시 코드 수정 필요 | 생성자 인자만 바꾸기 |
| 테스트 편의 | 테스트용 가짜 객체 넣기 어려움 | 테스트용 가짜 객체 주입 쉬움 |
| 결합도 | 강하게 결합 | 느슨한 결합 |
| 구현체 교체 | 모든 사용처에서 수정 | 주입하는 곳만 바꾸면 됨 |
| 코드 재사용 | 어려움 | 쉬움 |
기존 방식이 뭐가 그렇게 불편한데?
- 요구사항이 변경되었다고 가정
: "DB 연동을 위해 기존의 MemoryMemberRepository 대신 JdbcMemberRepository로 바꾸기"
기존방식 (New 방식)에서 요구사항 변경
// 기존
private final MemberRepository memberRepository = new MemoryMemberRepository();
// 수정 필요 (여기 직접 수정해야 함)
private final MemberRepository memberRepository = new JdbcMemberRepository();
- 문제점 1 : 모든 코드에서 new MemoryMemberRepository()로 쓰고 있었다면 전부 수정 필요
- 문제점 2 : 이 클래스가 테스트 중이었다면 테스트용 가짜 객체로 바꾸기 어려움
- 문제점 3 : 운영/테스트/로컬 등 환경에 따라 구현체를 바꾸고 싶어도 코드 레벨에서 고정되어 있어서 불가능함
의존성 주입 방식에서 요구사항 변경
// Main.java
// 어떤 구현체를 쓸지는 외부에서 결정
public class Main {
public static void main(String[] args) {
MemberRepository repo = new JdbcMemberRepository(); // 여기만 수정
OrderService service = new OrderService(repo);
System.out.println(service.order());
}
}
- OrderService는 손대지 않고, 바꾸고 싶은 구현체만 교체해서 주입하면 요구사항 변경 가능
Future
최근 부쩍 몸무게가 늘었다. 부트캠프 핑계로 운동을 안했더니 그런가보다. 운동을 안해서 그런지 체력도 더 떨어진 거 같다. 다시 운동을 시작해야겠다...
'PLAYDATA 주간회고' 카테고리의 다른 글
| 플레이데이터 풀스택 백엔드 9기 6월 1주차 회고 (1) | 2025.06.09 |
|---|---|
| 플레이데이터 풀스택 백엔드 9기 5월 5주차 회고 (0) | 2025.06.01 |
| 플레이데이터 풀스택 백엔드 9기 5월 3주차 회고 (0) | 2025.06.01 |
| 플레이데이터 풀스택 백엔드 9기 5월 2주차 회고 (0) | 2025.06.01 |
| 플레이데이터 풀스택 백엔드 9기 5월 1주차 회고 (0) | 2025.06.01 |