1. 프레임워크와 라이브러리의 차이
- 프레임워크: 애플리케이션의 구조를 제공하며, 제어의 흐름을 프레임워크가 관리(제어의 역전, IoC)함.
- 예: Spring, Django, Ruby on Rails
- 라이브러리: 특정 기능을 수행하는 코드 묶음이며, 개발자가 직접 호출하여 사용.
2. SOLID 원칙 (객체 지향 설계 원칙)
- SRP (단일 책임 원칙, Single Responsibility Principle)
- 클래스는 하나의 책임만 가져야 하며, 여러 책임이 집중될 경우 유지보수가 어려워짐.
- OCP (개방-폐쇄 원칙, Open/Closed Principle)
- 기존 코드를 수정하지 않고 기능을 확장할 수 있도록 설계해야 함. (인터페이스, 추상 클래스 활용)
- LSP (리스코프 치환 원칙, Liskov Substitution Principle)
- 부모 타입 객체를 자식 클래스 객체로 대체하더라도 프로그램이 정상 동작해야 함.
- ISP (인터페이스 분리 원칙, Interface Segregation Principle)
- 범용 인터페이스 하나보다는 특정 클라이언트를 위한 여러 개의 인터페이스가 나음.
- DIP (의존성 역전 원칙, Dependency Inversion Principle)
- 고수준 모듈(비즈니스 로직)은 저수준 모듈(데이터 접근 등)에 의존하지 말아야 하며, 추상화된 인터페이스에 의존해야 함.
3. 객체 지향 프로그래밍(OOP) 4대 특징
- 추상화(Abstraction): 불필요한 세부 정보를 숨기고 공통적인 특징을 모델링함.
- 캡슐화(Encapsulation): 데이터를 외부에서 직접 접근하지 못하도록 보호.
- 상속(Inheritance): 기존 클래스의 속성과 동작을 재사용하여 코드 중복을 방지.
- 다형성(Polymorphism): 같은 인터페이스나 메서드가 여러 구현체에서 다양한 방식으로 동작할 수 있도록 함.
4. 스프링 핵심 개념
4.1 IoC (Inversion of Control, 제어의 역전)
- 객체의 생성과 생명주기 관리를 개발자가 아닌 스프링 컨테이너가 담당함.
4.2 DI (Dependency Injection, 의존성 주입)
- 외부에서 객체의 의존성을 주입받아 런타임에 의존 관계를 설정하는 방식.
1
2
3
4
5
6
7
8
9
| @Component
public class MyService {
private final MyRepository myRepository;
@Autowired
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
|
4.3 스프링 컨테이너와 빈
- BeanFactory: 기본적인 컨테이너로, 빈(Bean)의 생성과 의존성 주입을 담당.
- ApplicationContext: BeanFactory를 확장한 컨테이너로, 추가적인 기능(이벤트 처리, 메시지 처리 등)을 제공.
빈(Bean): 스프링 컨테이너에서 관리하는 객체. 일반적으로 @Component, @Service, @Repository, @Controller 등의 어노테이션을 사용하여 빈으로 등록함. ```java @Configuration public class AppConfig { @Bean public MemberRepository memberRepository() { return new MemoryMemberRepository(); }
@Bean public MemberService memberService() { return new MemberServiceImpl(memberRepository()); } }
public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); MemberService memberService = applicationContext.getBean(“memberService”, MemberService.class); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| - 위의 예시 코드에서는 AppConfig 클래스에서 설정 정보를 제공하며, @Bean 메서드를 통해 객체를 스프링 컨테이너에 등록한다.
- `ApplicationContext`는 설정 정보를 읽어 MemberService와 같은 스프링 빈을 관리한다. (명시적 컨테이너 생성)
---
### 4.4 @ComponentScan
`@ComponentScan`은 스프링이 특정 패키지를 스캔하여 `@Component`가 붙은 모든 클래스를 자동으로 스프링 빈으로 등록하도록 설정하는 기능. `ApplicationContext`를 생성할 필요가 없음.
- **자동 빈 등록**: `@Component` 어노테이션이 붙은 클래스는 자동으로 스프링 빈으로 등록.
- 이를 포함하는 어노테이션(`@Controller`, `@Service`, `@Repository`)도 동일하게 동작.
- **패키지 스캔 위치 지정**: `@ComponentScan`의 `basePackages` 속성을 사용하여 스캔할 패키지를 지정할 수 있다. 지정하지 않으면 기본적으로 `@ComponentScan`이 선언된 클래스의 패키지와 하위 패키지를 스캔.
---
## 5. 의존성 주입(DI) 방법
| 주입 방식 | 장점 | 단점 | 추천 사용 시점 |
|------------|------|------|--------------|
| 생성자 주입 | 불변성 보장, 테스트 용이 | 의존성 많을 경우 코드 복잡 | 기본적으로 사용 |
| 수정자 주입 | 선택적 의존성 주입 가능 | 불변성이 약화됨 | 선택적 의존성 필요할 때 |
| 필드 주입 | 코드 간결 | 테스트 어려움, 유지보수 어려움 | 테스트 코드 외 사용 지양 |
```java
@Component
public class OrderService {
private final MemberRepository memberRepository;
@Autowired
public OrderService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
|
- 스프링 컨테이너는
@Autowired
어노테이션을 통해 의존 관계를 자동으로 주입한다. - 빈이 여러 개일 경우, 필드 이름이나
@Qualifier
로 특정 빈을 선택할 수 있다. - 생성자 주입은
final
키워드를 사용하여 값이 설정되지 않았다면 컴파일 오류를 일으키도록 유도할 수 있다.
6. 싱글톤 패턴과 스프링 컨테이너
6.1 싱글톤 패턴의 문제점
- 객체 생성 제한을 직접 구현해야 함.
- 멀티스레드 환경에서 동기화 문제가 발생할 수 있음.
6.2 스프링 컨테이너의 싱글톤 관리
| 비교 항목 | @ComponentScan
자동 등록 | @Configuration
+ @Bean
수동 등록 | |———–|——————|——————| | 빈 등록 방식 | @Component
, @Service
, @Repository
등의 애너테이션을 사용해 자동 등록 | @Bean
을 사용해 명시적으로 등록 | | 제어 수준 | 자동 등록이므로 세밀한 설정이 어려움 | 빈 생성 로직을 직접 제어 가능 | | 클래스 간 관계 | 의존성 주입이 필요한 경우 @Autowired
를 사용해야 함 | @Bean
메서드 간 호출을 통해 의존성을 직접 관리 가능 | | 싱글톤 보장 | 싱글톤이지만 @Component
빈끼리는 직접 메서드 호출 시 새로운 객체가 생성될 수 있음 | CGLIB 프록시를 사용하여 메서드 내부에서 호출해도 싱글톤 유지 |
1
2
3
4
5
6
7
| @Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
}
|
7. AOP (Aspect-Oriented Programming)
- 공통 관심사를 모듈화하여 비즈니스 로직과 분리하는 기법 (예: 로깅, 트랜잭션 관리)
1
2
3
4
5
6
7
8
| @Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Executing: " + joinPoint.getSignature().getName());
}
}
|
8. 트랜잭션 관리 (@Transactional)
@Transactional
을 사용하여 트랜잭션을 선언적으로 관리.- 기본적으로
RuntimeException
발생 시 롤백.
1
2
3
4
5
6
7
| @Service
public class UserService {
@Transactional
public void createUser(User user) {
// 사용자 생성 로직
}
}
|
9. 스케줄링 (@Scheduled)
- 특정 시간 간격 또는 정해진 시간에 실행되는 작업을 설정할 수 있음.
1
2
3
4
5
6
7
8
9
| @Scheduled(fixedRate = 5000)
public void fixedRateTask() {
System.out.println("5초마다 실행");
}
@Scheduled(cron = "0 0 12 * * ?")
public void cronTask() {
System.out.println("매일 12시에 실행");
}
|
@EnableScheduling
을 추가해야 스케줄링이 활성화됨
1
2
3
4
5
6
7
| @SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
|
10. 스프링 부가 기능
- Validation:
@NotNull
, @Size
, @Min
, @Max
등을 사용하여 데이터 검증. - Data Binding: HTTP 요청 파라미터를 자바 객체로 변환.
- SpEL (Spring Expression Language): 표현식 언어를 활용한 동적 설정 가능.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| @Component
public class MyBean {
private String name = "Spring";
public String getName() {
return name;
}
}
@Configuration
public class AppConfig {
@Value("#{myBean.name}")
private String beanName;
}
|
Lombok 활용
Lombok을 활용하여 생성자 주입을 간소화할 수 있다. @RequiredArgsConstructor 애너테이션을 사용하면 final 필드나 @NonNull이 붙은 필드들을 대상으로 자동으로 생성자가 생성된다. 이를 통해 스프링 프레임워크에서 일반적으로 사용하는 @Autowired 애너테이션을 생성자 주입 시 명시하지 않아도 의존성 주입이 가능하다. 스프링에서는 생성자가 하나뿐인 경우 자동으로 해당 생성자를 통해 의존성을 주입하기 때문에, @RequiredArgsConstructor를 활용하면 코드의 간결성과 가독성을 높일 수 있다.
@Autowired할 빈이 2개 이상.
@Autowired 필드 명 매칭 @Qualifier @Qualifier끼리 매칭 빈 이름 매칭 @Primary 사용
Tips
- IntelliJ 설정-> Build, Execution, Deployment -> Build Tools -> Gradle 에 들어가서
Build and run using
과 Run tests using
을 IntelliJ IDEA
로 바꿔주면 더 빠르다.