스프링의 다른 프레임워크와 가장 큰 차이점은다른 프레임워크들의 포용이다. 이는 다시말해 기본 뼈대를 흔들지 않고, 여러 종류의 프레임워크를 혼용해서 사용할 수 있다는 점.
② 스프링 프레임워크 특징
POJO(Plan Old Java Object)기반의 구성 - 코드를 개발할 때 개발자가특정한 라이버리나 컨테이너의 기술에 종속적이지 않다는 것을 의미 - 쉽게말해 Java코드를 이용해서 객체를 구성하는 방식을 그대로 스프링에서 사용할 수 있다는 의미
의존성 주입 - 의존성 주입은 하나의 객체가 다른객체의 의존성을 제공하는 테크닉 -"어떤 객체가 필요한 객체를 외부에서 밀어 넣는다."는 의미 - "의존성 주입"을 사용하여 갖는 장점은 '주입을 받는 입장에서는 어떤 객체인지 신경 쓸 필요가 없다.','어떤 객체에 의존하든 자신의 역할은 변하지 않는다.'이다. -"ApplicationContext"라는 존재가 필요한 객체들을 생성하고, 필요한 객체들을 주입하는 역할을 해주는 구조 - "ApplicationContext가 관리하는 객체들을 "빈(Bean)"이라고 한다. - 빈과 빈 사이의 의존 관계를 'xml 설정', '어노테이션 설정', 'Java 설정 방식'을 통해서 처리 할 수 있다.
관점지향프로그래밍(AOP)의 지원 - 대부분의 시스템에서 '보안'이나 '로그', '트랜잭션'과 같이 무엇을 실행을 하든 반드시 처리가 필요한 부분이 있다. 이를 '횡단 관심사(cross-concern)'라고 한다. - AOP는 이러한 횡단 관심사를 모듈로 분리하는 프로그래밍 패러다임이다. - 이 패러다임을 통해서반복적인 코드를 줄이고, 핵심 비즈니스 로직에만 집중을 할 수 있다.
높은 확장성 - 스프링 프레임워크에 통합하기 위해 간단하게 기존 라이브러리를 감싸는 정도로 스프링에서 사용이 가능하다. - 따라서 수많은 라이브러리가 이미 스프링에서 지원되고 있고 스프링에서 사용되는 라이브러리를 별도로 분리하기도용이하다.
③ 스프링 프레임워크 사용이유(장점)
정형화 되어 있기 때문에 일정수준의 품질을 기대 할 수 있다.
개발 후 유지보수 및 기능의 확장성에서도 품질을 보장한다.
'전자표준 프레임워크'기 때문에 해당 프레임워크의 수요가 크다.(한국 한정)
④ 스프링 프레임워크 단점
내부에서 많은 기능을 가졌기 때문에 상당히 무겁다.
분명 많은 기능을 제공해서 편리하지만, 이 기능들을 습득하기위해 상당한 노력과 시간이 필요하다.
@AllArgsConstructor
public class Order {
private String food;
private int price;
private String makers;
/*@AllArgsConstructor를 사용하면 아래와 같은 생성자를 자동 생성할 수 있다.
public Order(String food, int price, String makers) {
this.food = food;
this.price = price;
this.makers = makers;
}
*/
}
@NoArgsConstructor
어떠한 변수도 사용하지 않는 기본 생성자를 자동완성 어노테이션이다.
@NoArgsConstructor
public class Order {
private String food;
private int price;
private String makers;
/*@NoArgsConstructor 사용하면 아래와 같은 생성자를 자동 생성할 수 있다.
public Order() {
}
*/
}
@RequireArgsConstructor
특정 변수만을 활용하는 생성자를 자동완성 시켜주는 어노테이션이다. 생성자의 인자로 추가할 변수에 @NonNull 어노테이션을 붙여서 해당 변수를 생성자의 인자로 추가할 수 있다. 아니면 해당 변수를 final로 선언해도 의존성을 주입받을 수 있다.
@RequireArgsConstructor
public class Order {
@NotNull
private String food;
private final int price;
private String makers;
/*@RequireArgsConstructor 사용하면 아래와 같은 생성자를 자동 생성할 수 있다.
public Order(String food, int price) {
this.food = food;
this.price = price;
}
*/
}
@Builder(번외) Builder 패턴이란 점증적 생성자 패턴과 자바빈즈 패턴의 단점을 보완하기 위해 사용되는 디자인패턴이다. @Builder 어노테이션을 사용하면 클래스 객체 생성시 Builder 패턴을 적용시켜준다. 모든 변수들에 대해 build하기를 원한다면 클래스 위에 @Builder를 붙이면 되지만, 특정 변수만을 build하기 원한다면 생성자를 작성하고 그 위에 @Builder 어노테이션을 붙여주면 된다.
Builder 패턴을 적용시 장점
불필요한 생성자의 제거
데이터의 순서에 상관없이 객체생성 가능
명시적 선언으로 이해하기가 쉬우며 각 인자가 어떤 의미인지 알기 쉽다.
setter메서드가 없으므로 변경 불가능한 객체를 만들수있다.
정보들을 다 받은 후에 한번에 객체를 생성하므로 객체일관성이 깨지지 않는다.
build()함수가 null인지 체크해주므로 검증이 가능한다.
의존성 주입 방법
생성자 주입(Constructor Injection): 생성자를 통해 의존 관계를 주입하는 방법
@Service
public class UserService {
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
@Autowired
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
}
생성자가 의존성 주입되는 클래스 내에 1개만 존재할 경우 @Autowired 어노테이션을 생략할 수 있다. 생성자 주입을 이용하면 필드 객체에 final(더이상 수정 불가능) 키워드를 사용할 수 있으며, 컴파일 시점에 누락된 의존성을 확인할 수 있다.
@Service
@RequireArgsConstuctor
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
}
주입 시킬 객체(UserRepository, PasswordEncoder)에 final 키워드를 붙임으로써 UserService가 스프링 컨테이너에 Bean으로 등록될 때 UserRespository와 PasswordEncoder를 주입시켜준다.
수정자 주입(Setter Injection): Setter를 통해 의존 관계를 주입하는 방법
@Service
public class UserService {
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
@Autowired
public setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public serPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
}
주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.
필드 주입(Field Injection): 필드에 바로 의존 관계를 주입하는 방법
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
}
필드 주입은 외부에서 접근이 불가능하다는 단점이 존재한다. 또한, 필드 주입은 DI 프레임워크가 존재해야 하므로 프레임 워크와의 의존성이 강하게 결합된다. 따라서 애플리케이션의 구동에 무관한 테스트 코드를 작성하거나 불가피하게 설정해야할 경우를 제외하고 사용을 지양해야한다.
실제 개발 상황에서는 의존 관계의 변경이 필요한 경우는 드물다. 수정자 주입과 같이 수정의 가능성을 열어둔 상태로 개발을 진행하게 되면 유지 보수성이 떨어진다. 그러므로 의존 관계 주입시, 생성자 주입을 통해 변경의 여지를 두지 않고 불변성을 보장하는 것이 좋다.
생성자 주입을 사용할 경우의 장점
① 순환 참조 방지
-> 애플리케이션 구동시 객체의 생성 시점에서 에러(컴파일 에러)가 발생하므로 순환 참조 문제를 방지할 수 있다.
② 테스트 코드 작성 용이
-> 생성자 주입을 사용하면 컴파일 시점에 객체를 주입받아 테스트 코드를 작성할 수 있으며, 주입하는 객체가 누락된 경우 컴파일 시점에 오류를 발견할 수 있다
③ 객체의 불변성을 확보할 수 있으며, 객체의 변이를 방지한다.
-> 애플리케이션이 구동되어 @Component 어노테이션을 통해 Spring Bean으로 등록된 final 키워드를 가진 객체들은 객체 생성 이후 내부 상태가 변화하지 않는다.(read-only 메소드만 제공)
인증(Authentication) : 해당 유저가 실제 유저인지 인증하는 개념. 스마트폰에 지문인식, 이용하는 사이트에 로그인 등과 같이, 실제 그 유저가 맞는지를 확인하는 절차를 의미한다. 일반적으로 웹 어플리케이션은 아래 설명할 두가지 방법을 통해서 인증을 처리한다.
인가(Authorization) : 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념. 예를들어 관리자 페이지-관리자 권한 같은 것들이 해당된다.
웹 어플리케이션 인증의 특수성
일반적으로 서버-클라이언트 구조로 되어있고, 실제로 이 두가지 요소는 아주 멀리 떨어져 있다.
그리고 Http 라는 프로토콜을 이용하여 통신하는데, 그 통신은 비연결성(Connectionless) 무상태(Stateless)로 이루어진다.
보통 모바일/웹 서비스의 인증은 HTTP 메세지의 헤더에 인증 수단을 넣어 요청을 보낸다.
비연결성(Connectionless)은 서버와 클라이언트가 연결되어 있지 않다는 것. 채팅이나 게임 같은 것들을 하지 않는 이상 서버와 클라이언트는 실제로 연결되어 있지 않다. 그 이유는 리소스를 절약하기 위해서 인데, 만약 서버와 클라이언트가 실제로 계속 연결되어있다면 클라이언트는 그렇다고 쳐도, 서버의 비용이 기하급수적으로 늘어나기 때문. 그래서 서버는 실제로 하나의 요청에 하나의 응답을 내버리고 연결을 끊어버리고있다 라고 생각하면 된다.
무상태(Stateless)는 서버가 클라이언트의 상태를 저장하지 않는다는 것. 기존의 상태를 저장하는 것들도 마찬가지로 서버의 비용과 부담을 증가시키는 것 이기 때문에 기존의 상태가 없다고 가정하는 프로토콜을 이용해 구현되어 있다. 실제로 서버는 클라이언트가 직전에, 혹은 그 전에 어떠한 요청을 보냈는지 관심도 없고 전혀 알지 못한다.
인증방식 (1) 쿠키 - 세션 방식
쿠키-세션 방식은 서버가 특정 유저가 로그인 되었다는 상태를 저장하는 방식
세션은 서버에서 가지고 있는 정보이며 쿠키는 사용자에게 발급된 세션을 열기 위한 열쇠(SESSION ID)를 의미
인증과 관련된 아주 약간의 정보만 서버가 가지고 있게 되고 유저의 이전 상태의 전부는 아니더라도 인증과 관련된 최소한의 정보는 저장해서 로그인을 유지시킨다는 개념
결과적으로 Session/Cookie 인증 방식을 사용하는 이유는 서버에 인증의 책임을 전가하는 것이다.
실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 “세션 저장소”에 해당 유저가 로그인 되었다는 정보를 넣습니다.
세션 저장소에서는 유저의 정보와는 관련 없는 난수인 session-id를 발급합니다.
서버는 로그인 요청의 응답으로 session-id를 내어줍니다.
클라이언트는 그 session-id를 쿠키라는 저장소에 보관하고 앞으로의 요청마다 세션아이디를 같이 보냅니다. (주로 HTTP header에 담아서 보냅니다!)
클라이언트의 요청에서 쿠키를 발견했다면 서버는 세션 저장소에서 쿠키를 검증합니다.
만약 유저정보를 받아왔다면 이 사용자는 로그인이 되어있는 사용자겠죠?
이후에는 로그인 된 유저에 따른 응답을 내어줍니다.아래는 그림의 각 번호에 따른 설명
① 장점
세션/쿠키 방식은 기본적으로 쿠키를 매개로 인증을 거치기 때문에 비교적 안전한 방식이다.
여기서 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠를 의미한다.
따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체(세션 ID)는 유의미한 값을 갖고있지 않다(중요 정보는 서버 세션)
헤더에 직접적으로 계정정보를 담아 인증을 거치는 것 보단 안전하다.
사용자 A는 1번, 사용자 B는 2번 이런식으로 고유의 ID값을 발급받게 된다. 그렇게 되면 서버에서는 쿠키 값을 받았을 때 일일이 회원정보를 확인할 필요 없이 바로 어떤 회원인지를 확인할 수 있어 서버의 자원에 접근하기 용이하다.
② 단점
장점 1에서 쿠키를 탈취당하더라도 안전할 수 있다고 언급했지만 문제가 하나 있다. 만일 A 사용자의 HTTP 요청을 B 사용자(해커)가 가로챘다면 그 안에 들어있는 쿠키도 충분히 훔칠 수 있다. 그리고 B 사용자는 그 훔친 쿠키를 이용해 HTTP 요청을 보내면 서버의 세션저장소에서는 A 사용자로 오인해 정보를 잘못 뿌려주게 된다.(세션 하이재킹 공격이라고 한다)
실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 유저의 정보를 JWT로 암호화 해서 내보냅니다
서버는 로그인 요청의 응답으로 jwt 토큰을 내어줍니다.
클라이언트는 그 토큰을 저장소에 보관하고 앞으로의 요청마다 토큰을 같이 보냅니다.
클라이언트의 요청에서 토큰을 발견했다면 서버는 토큰을 검증합니다.
이후에는 로그인 된 유저에 따른 응답을 내어줍니다.
① 장점
간편하다. 세션/쿠키는 별도의 저장소의 관리가 필요하다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이는 Stateless 한 서버를 만드는 입장에서는 큰 강점이다. 여기서 Stateless는 어떠한 별도의 저장소도 사용하지 않는, 즉 상태를 저장하지 않는 것을 의미. 이는 서버를 확장하거나 유지,보수하는데 유리하다.
확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어 Facebook 로그인, Google 로그인, 카카오 OAuth2 로그인 등은 모두 토큰을 기반으로 인증을 한다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다.
동시 접속자가 많을 때 서버 측 부하를 낮춰줄 수 있다.
② 단점
이미 발급된 JWT에 대해서는 돌이킬 수 없다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 된다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능하다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 신나게 정보들을 털어갈 수 있다.
기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급한다. 그렇게 되면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있다.
Payload 정보가 제한적이다. 위에서 언급했다시피 Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관된다) 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없다.
JWT의 길이. 세션/쿠키 방식에 비해 JWT의 길이는 길다. 따라서 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생하게 된다. = 비용 증가
구현의 복잡도가 증가한다.
Secret key 유츌 시 JWT 조작 가능하다.
쿠키세션과 JWT의 차이
가장 큰 차이점은 세션/쿠키는 세션 저장소에 유저의 정보를 넣는 반면, JWT는 토큰 안에 유저의 정보들이 넣는다는 점
물론 클라이언트 입장에서는 HTTP 헤더에 세션ID나 토큰을 실어서 보내준다는 점에서는 동일하나, 서버 측에서는 인증을 위해 암호화를 하냐, 별도의 저장소를 이용하냐는 차이가 발생한다.
1. 처음 설계한 API와 ERD에 변경사항이 있었나요? 변경되었다면 어떤 점 때문일까요? 첫 설계의 중요성에 대해 생각해보세요.
과제 레벨 1 까지만 구현했기 때문에 처음 설계한 API와 ERD에 큰 변경사항은 없었지만, 만약 레벨2로 넘어가야 했다면, 게시문의 댓글을 담당하는 Comment Entity를 하나 더 생성하고 FK를 이용해 게시글과 맵핑해주는 작업을 추가하는 수정이 필요했을 것 같다.
2. ERD를 먼저 설계한 후 Entity를 개발했을 때 어떤 점이 도움이 되셨나요?
ERD(Entity Relationship Diagram)
엔티티와 이들 간의 관계를 알기 쉽게 약속된 도형을 이용하여 일목요연하게그림으로 표현한 것이다.
ER모델의 구성요소는엔티티(Entity), 관계(Relationship), 속성(Attribute) 을 기본으로 하여 관계 수, 식별자, 서브타입 등으로 세분화 할 수 있다.
문장 형식의 업무 처리 규정을 약속된 도형 형태로 나타내어전체 업무 및 데이터의 구조를 쉽게 파악할 수 있다.
문장으로 기술하지 않고 공통적인 약속을 통해 표현함으로써업무의 파악과 이해가 용이하다.
데이터베이스를 구현할 때 정규화(Normalization)된 테이블을 만들기 위한 근거 자료로 활용한다.
3. JWT를 사용하여 인증/인가를 구현 했을 때의 장점은 무엇일까요?
간편하다.세션/쿠키는 별도의 저장소의 관리가 필요하다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이는 Stateless 한 서버를 만드는 입장에서는 큰 강점이다. 여기서 Stateless는 어떠한 별도의 저장소도 사용하지 않는, 즉 상태를 저장하지 않는 것을 의미. 이는서버를 확장하거나 유지,보수하는데 유리하다.
확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어 Facebook 로그인, Google 로그인, 카카오 OAuth2 로그인 등은 모두 토큰을 기반으로 인증을 한다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다.
동시 접속자가 많을 때 서버 측 부하를 낮춰줄 수 있다.
4. 반대로 JWT를 사용한 인증/인가의 한계점은 무엇일까요?
이미 발급된 JWT에 대해서는 돌이킬 수 없다.세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 된다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능하다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 신나게 정보들을 털어갈 수 있다.
Payload 정보가 제한적이다. 위에서 언급했다시피 Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관된다) 따라서유저의 중요한 정보들은 Payload에 넣을 수 없다.
JWT의 길이. 세션/쿠키 방식에 비해 JWT의 길이는 길다. 따라서인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생하게 된다. = 비용 증가
구현의 복잡도가 증가한다.
Secret key 유츌 시 JWT 조작 가능하다.
5. 만약 댓글 기능이 있는 블로그에서 댓글이 달려있는 게시글을 삭제하려고 한다면 무슨 문제가 발생할까요? Database 테이블 관점에서 해결방법이 무엇일까요?
게시글에 달려있는 댓글 또한 동시에 삭제해야 한다. @OneToMany(cascade= CASCADE.REMOVE)를 사용하여 게시글이 삭제 될 때 그와 연관된 여러개의 댓글도 동시에 삭제되도록 해결할 수 있을 것 같다.
6. IoC / DI 에 대해 간략하게 설명해 주세요!
DI(Dependency Injection)
DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능이다.
객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.
DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.
Ioc(Inversion of Control)
IoC(Inversion of Control)란 "제어의 역전" 이라는 의미로, 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다.
IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다"라고 한다.
객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.
유효기간이 짧은 Token의 경우 그만큼 사용자는 로그인을 자주 해서 새롭게 Token을 발급받아야 하므로 불편합니다.
그러나 유효기간을 늘리자면, 토큰을 탈취당했을 때 보안에 더 취약해지게 됩니다.
이때 “그러면 유효기간을 짧게 하면서좋은 방법이 있지는 않을까?”라는 질문의 답이 바로"Refresh Token"입니다.
해결법
Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인을 완료했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다(여기서 만료라는 개념은 그냥 유효기간을 지났다는 의미입니다.)
Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간이라 하겠습니다. 사용자는 API 요청을 신나게 하다가 1시간이 지나게 되면, 가지고 있는 Access Token은 만료됩니다. 그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있습니다.
* Access Token은 탈취당하면 정보가 유출되는건 동일합니다. 다만 짧은 유효기간 안에만 사용이 가능하기에 더 안전하다는 의미입니다.
* Refresh Token의 유효기간이 만료됐다면, 사용자는 새로 로그인해야 합니다. Refresh Token도 탈취될 가능성이 있기 때문에 적절한 유효기간 설정이 필요해보입니다(보통 2주로 많이 잡더군요)
참고 자료 : https://tansfil.tistory.com/59?category=475681
JWT 장단점
장점
동시 접속자가 많을 때 서버 측 부하 낮춤
Client, Sever 가 다른 도메인을 사용할 때 - 예) 카카오 OAuth2 로그인 시 JWT Token 사용
단점
구현의 복잡도 증가
JWT 에 담는 내용이 커질 수록 네트워크 비용 증가 (클라이언트 → 서버)
기 생성된 JWT 를 일부만 만료시킬 방법이 없음
Secret key 유출 시 JWT 조작 가능
느슨한 결합 / 강한 결합
강한 결합
객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 가지는 구조이다. A 클래스 내부에서 B 라는 객체를 직접 생성하고 있다면, B 객체를 C 객체로 바꾸고 싶은 경우에 A 클래스도 수정해야 하는 방식이기 때문에 강한 결합이다.
느슨한 결합
객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해서 넘겨받는 것이다. 이렇게 하면 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기 때문에 유연한 구조를 가진다.
강한 결합에서 느슨한 결합으로 바뀌는 것을 IoC(제어 반전)이라고 하고, SOLID 원칙에서 O 에 해당하는 Open Closed Principle 을 지키기 위해서 IoC를 한다고 생각할 수 있다.
*Open Closed Principle란 객체 지향의 5대 원칙 중 하나로, 확장에는 열려(Open) 있으나, 변경에는 닫혀(Closed)있어야 한다는 것을 의미한다. (유지 보수를 위해 처음부터 설계를 잘해야 한다는 의미인듯)