본문 바로가기
-- 오늘 있었던 개발 일기

Spring 프레임워크가 무엇인가

by code study 2025. 12. 26.

개념 설명

Spring은 Java로 웹사이트나 앱 서버를 만들 때 사용하는 도구 모음입니다. 2003년에 만들어져서 지금까지 전 세계 개발자들이 가장 많이 사용하고 있습니다.

 

Spring의 진짜 역할

 잘못된 이해

  • Spring = 단순히 라이브러리 모음?
  • Spring = Spring Boot?
  • Spring = 웹 프레임워크?

올바른 이해

  1. 객체의 생명주기를 관리하는 컨테이너
  2. 다양한 기술을 통합하는 플랫폼
@SpringBootApplication  // Spring Boot 애플리케이션임을 선언
public class MyApplication {
    public static void main(String[] args) {
        // Spring이 애플리케이션의 모든 객체를 자동으로 생성하고 관리
        SpringApplication.run(MyApplication.class, args);
    }
}

왜 Spring을 사용하는가?

Plain Java vs Spring 비교표

  Plain JAVA spring
객체 생성 직접 new 키워드로 생성 컨테이너가 자동 생성 및 관리
의존성 관리 직접 연결, 강한 결합 DI로 느슨한 결합
트랜잭션 처리 수동으로 commit/rollback @Transactional 어노테이션
테스트 Mock 객체 수동 생성 자동 의존성 주입

 

Plain Java 방식 - 문제점

public class UserService {
    // 문제 1: 구체적인 구현 클래스에 직접 의존 (강한 결합)
    private UserRepository userRepository = new UserRepositoryImpl();

    public User findUser(Long id) {
        Connection conn = null;
        try {
            // 문제 2: DB 연결을 직접 관리해야 함
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            conn.setAutoCommit(false);  // 트랜잭션 시작

            User user = userRepository.findById(id);

            conn.commit();  // 성공 시 커밋
            return user;
        } catch (Exception e) {
            // 문제 3: 에러 처리도 직접 작성
            if (conn != null) conn.rollback();
            throw e;
        } finally {
            // 문제 4: 리소스 정리도 직접 해야 함
            if (conn != null) conn.close();
        }
    }
}

Spring 방식 - 개선점

@Service  // Spring에게 "이 클래스를 빈으로 등록해줘"
public class UserService {
    // 개선 1: 인터페이스에만 의존 (느슨한 결합)
    private final UserRepository userRepository;

    // 개선 2: 생성자를 통해 의존성 주입 받음
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 개선 3: @Transactional 하나로 트랜잭션 자동 처리
    @Transactional
    public User findUser(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }
}

코드가 훨씬 간결해지고 개발자는 비즈니스 로직에만 집중할 수 있다.


Spring의 3대 핵심 개념

1️⃣ IoC/DI - 제어의 역전 & 의존성 주입

IoC (Inversion of Control)

프로그램의 제어 흐름을 개발자가 아닌 프레임워크가 담당하는 디자인 원칙

❌ 전통적인 방식 (개발자가 제어)

public class OrderService {
    public void processOrder() {
        // 개발자가 직접 객체를 생성하고 관리
        PaymentService paymentService = new PaymentService();
        paymentService.processPayment();

        NotificationService notificationService = new NotificationService();
        notificationService.sendNotification();
    }
}

✅ IoC 방식 (Spring이 제어)

@Service
public class OrderService {
    // Spring이 생성한 객체를 받아서 사용만 함
    private final PaymentService paymentService;
    private final NotificationService notificationService;

    // Spring이 자동으로 필요한 객체들을 생성해서 주입
    public OrderService(PaymentService paymentService, 
                        NotificationService notificationService) {
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }

    public void processOrder() {
        paymentService.processPayment();
        notificationService.sendNotification();
    }
}

 

DI의 세 가지 방식

Ioc를 구현하는 구체적인 방법

 

1.생성자 주입 (권장) 

@Service
public class UserService {
    private final UserRepository userRepository;  
    //  final 키워드로 불변성을 보장하고 순환 참조를 방지할 수 있다.

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

 

2.Setter 주입

@Service
public class UserService {
    private UserRepository userRepository;  // final 사용 불가

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

 

3.필드 주입 (비권장) ❌

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // 테스트 어려움
}

생성자 주입을 권장하는 이유

방식장점단점
생성자 주입 final 사용, 불변성 보장, 순환 참조 방지 생성자 코드가 길어질 수 있음
Setter 주입 선택적 의존성, 런타임 변경 가능 불변성 보장 안됨, NPE 위험
필드 주입 코드 간결 테스트 어려움, final 불가

2️⃣ AOP - 관점 지향 프로그래밍

핵심 비즈니스 로직과 부가 기능(로깅, 트랜잭션, 보안)을 분리하여 관리하는 프로그래밍 패러다임

❌ AOP 없이 (코드 중복)

@Service
public class UserService {
    public User createUser(User user) {
        // 부가 기능: 로깅 시작
        log.info("createUser 시작: {}", user);
        long startTime = System.currentTimeMillis();

        try {
            // 핵심 로직: 사용자 저장
            User savedUser = userRepository.save(user);

            // 부가 기능: 실행 시간 로깅
            log.info("createUser 완료: {}ms", 
                System.currentTimeMillis() - startTime);
            return savedUser;
        } catch (Exception e) {
            log.error("createUser 실패", e);
            throw e;
        }
    }

    public User updateUser(User user) {
        // 문제: 똑같은 로깅 코드를 또 작성... (중복)
        log.info("updateUser 시작: {}", user);
        // ... 반복
    }
}

 

✅ AOP 적용 (관심사 분리)

// Aspect: 공통 기능을 모아놓은 클래스
@Aspect
@Component
public class LoggingAspect {

    // service 패키지의 모든 메소드에 이 로직을 적용
    @Around("execution(* com.example.service.*.*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        log.info("{} 시작", joinPoint.getSignature().getName());

        try {
            Object result = joinPoint.proceed();  // 실제 메소드 실행
            
            log.info("{} 완료: {}ms", 
                joinPoint.getSignature().getName(),
                System.currentTimeMillis() - startTime);
            return result;
        } catch (Exception e) {
            log.error("{} 실패", joinPoint.getSignature().getName(), e);
            throw e;
        }
    }
}

// 비즈니스 로직은 핵심 기능만 집중
@Service
public class UserService {
    // 로깅 코드 없이 깔끔!
    public User createUser(User user) {
        return userRepository.save(user);
    }

    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

3️⃣ PSA - 이식 가능한 서비스 추상화

환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 설계 원칙

❌ 기술에 종속된 코드

public class UserRepository {
    public void save(User user) {
        // 문제: JDBC 기술에 직접 의존
        // JPA로 바꾸려면 이 코드 전부 수정해야 함
        Connection conn = DriverManager.getConnection(...);
        PreparedStatement pstmt = conn.prepareStatement(
            "INSERT INTO users VALUES (?, ?)");
        // ... JDBC 코드에 종속
    }
}

 

✅ PSA 적용 (추상화)

- 기술 변경에 유연하게 대응할 수 있게 해주는 설계 원칙

// JpaRepository를 상속받으면 기본 CRUD 자동 생성
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

@Service
public class UserService {
    private final UserRepository userRepository;

    public User saveUser(User user) {
        // 구현 기술(JDBC, JPA, MongoDB 등)과 무관하게 동일한 코드
        // 나중에 기술을 바꿔도 이 코드는 수정할 필요 없음
        return userRepository.save(user);
        //Spring Data JPA의 JpaRepository를 사용하면, save() 메소드 하나로 구현 기술과 무관하게 데이터를 저장할 수 있습니다.
    }
}

🆚 Spring vs Spring Boot

차이점 비교

  Spring  Spring Boot
설정 XML/Java Config 수동 설정 자동 설정
의존성 관리 버전 직접 관리 Starter로 자동 관리
내장 서버 별도 WAS 필요 내장 서버 포함
초기 설정 오래 걸림 몇 분 내 완료

❌ Spring Framework (복잡)

 
xml
<!-- web.xml -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
</servlet>

<!-- applicationContext.xml -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <!-- ... 수많은 설정 -->
</bean>

✅ Spring Boot (간단)

 
java
@SpringBootApplication  // 자동 설정을 모두 해줌
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
 
 
yaml
# application.yml (간단한 설정만)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: password

 핵심 정리

Spring의 3대 핵심 기술

  1. IoC/DI: 객체의 생명주기와 의존성을 Spring이 관리
  2. AOP: 공통 관심사를 분리하여 중복 제거
  3. PSA: 기술 변경에 유연한 추상화 계층 제공

Spring을 사용해야 하는 이유

  • 생산성: 반복 코드 감소, 비즈니스 로직 집중
  • 유지보수성: 느슨한 결합, 변경 용이
  • 테스트 용이성: DI로 Mock 테스트 간편
  • 커뮤니티: 방대한 생태계와 레퍼런스

Spring Boot의 장점

  • 자동 설정으로 개발 시간 단축
  • 내장 서버로 배포 간편
  • Starter 의존성으로 버전 관리 자동화

'-- 오늘 있었던 개발 일기' 카테고리의 다른 글

RESTful Api란?  (0) 2026.01.04
MCP 연결에 대해!  (0) 2025.12.26
NoSql에서 트랜잭션이 가능한가?  (0) 2025.12.22
RDBMS란 무엇인가?  (0) 2025.12.21
오늘의 개발 문제 : fetch였다..  (0) 2025.12.19