본문 바로가기
카테고리 없음

스프링 부트 입문 (IoC/DI, JPA, Spring Security)

by ricepuppy9733 2026. 6. 14.

솔직히 처음 Spring Boot를 접했을 때 가장 당황했던 부분은 new 키워드가 없어도 객체가 알아서 생겨난다는 점이었습니다. 분명히 어디서도 객체를 만든 적이 없는데 실행하면 콘솔에 "서비스 객체 생성됨"이 찍혀 있었습니다. 이게 도대체 왜 되는 건지 이해하는 데 꽤 시간이 걸렸고, 그 과정에서 Spring Boot의 핵심이 무엇인지 조금씩 보이기 시작했습니다.

스프링 개념

IoC와 DI, 객체를 내가 안 만들어도 되는 이유

Spring Boot의 핵심을 한 마디로 표현하면 제어의 역전, 즉 IoC(Inversion of Control)라고 할 수 있습니다. IoC란 객체 생성, 연결, 관리 같은 작업을 개발자가 직접 처리하지 않고 프레임워크가 대신 처리하는 방식을 말합니다. 원래 자바 코드에서는 new TestService()처럼 직접 인스턴스를 만들어야 했지만, Spring에서는 그럴 필요가 없습니다.

이 IoC를 구체적으로 실현하는 방식이 의존성 주입, DI(Dependency Injection)입니다. DI란 어떤 클래스가 필요로 하는 객체를 직접 생성하지 않고 외부에서 만들어서 넣어주는 방식입니다. @Autowired 어노테이션 하나만 붙여두면 Spring이 알아서 해당 타입의 객체를 찾아서 주입해줍니다. 제가 직접 써봤는데, 처음에는 이게 마법처럼 느껴졌습니다. 분명히 TestService testService; 한 줄만 적어뒀을 뿐인데 메서드 안에서 아무런 오류 없이 동작하고 있었으니까요.

이 과정이 가능한 이유는 컴포넌트 스캔(Component Scan) 때문입니다. 컴포넌트 스캔이란 Spring이 프로젝트를 시작할 때 특정 어노테이션이 달린 클래스를 자동으로 찾아 객체를 생성하고 관리하는 과정입니다. 스캔 대상이 되는 어노테이션은 다음과 같습니다.

  • @Component: 가장 기본적인 컴포넌트 등록 어노테이션
  • @Controller / @RestController: HTTP 요청을 처리하는 컨트롤러 계층
  • @Service: 비즈니스 로직을 담당하는 서비스 계층
  • @Repository: 데이터 접근 로직을 담당하는 레포지토리 계층
  • @Configuration: 설정 클래스 등록

이렇게 스캔된 객체들은 기본적으로 싱글톤(Singleton) 패턴으로 생성됩니다. 싱글톤 패턴이란 애플리케이션 전체에서 해당 클래스의 인스턴스가 단 하나만 존재하도록 보장하는 방식입니다. 예전에는 이걸 직접 구현하느라 별도의 코드를 작성해야 했는데, Spring이 이를 알아서 처리해준다는 점이 개발 생산성 측면에서 상당히 큰 이점입니다(출처: Spring Framework 공식 문서).

JPA로 SQL 없이 DB 연동하기

데이터베이스 연동을 처음 배울 때 저는 JDBC(Java Database Connectivity)를 먼저 접했습니다. JDBC란 자바 프로그램이 데이터베이스와 통신할 수 있도록 표준화된 인터페이스를 제공하는 API입니다. JdbcTemplate을 통해 SQL 문자열을 직접 작성하고 queryForObject, query, update 같은 메서드를 호출해서 DB와 통신하는 방식이었습니다. 솔직히 이건 예상 밖이었습니다. 이전에 순수 자바로 DB를 연결할 때는 ResultSet으로 컬럼 하나하나를 꺼내서 DTO에 옮겨 담아야 했는데, BeanPropertyRowMapper 하나로 그 과정이 통째로 사라졌으니까요.

그런데 JDBC에서 한 단계 더 나아간 것이 JPA(Java Persistence API)입니다. JPA란 자바 객체와 관계형 데이터베이스 테이블을 자동으로 연결해주는 ORM(Object-Relational Mapping) 표준 명세입니다. ORM이란 객체 지향 프로그래밍의 클래스와 데이터베이스의 테이블을 자동으로 대응시켜 SQL을 직접 작성하지 않아도 되게 만들어주는 기술입니다. Spring Boot에서는 JPA의 구현체로 하이버네이트(Hibernate)를 기본으로 사용합니다.

실제로 써보면 차이가 확연합니다. JDBC 방식에서는 INSERT INTO board (title, contents) VALUES (?, ?)라고 직접 SQL을 적어야 했지만, JPA에서는 boardRepository.save(entity) 한 줄로 끝납니다. application.ymlddl-auto: update 설정만 해두면 엔티티 클래스 구조를 읽어서 테이블을 자동으로 생성하거나 수정해줍니다. 제 경험상 이건 초반에 DB 설계가 자주 바뀌는 프로젝트에서 특히 유용했습니다.

다만 JPA가 무조건 편한 것만은 아닙니다. JDBC는 DTO 하나로 요청과 응답을 모두 처리할 수 있는 반면, JPA는 DB와 매핑되는 엔티티(Entity) 클래스를 별도로 만들어야 합니다. 엔티티란 데이터베이스 테이블과 1대1로 매핑되는 자바 클래스로, @Entity 어노테이션으로 선언합니다. 클라이언트와 통신할 때는 엔티티를 그대로 노출하면 안 되고 반드시 DTO를 통해 필요한 데이터만 골라서 전달해야 합니다. 보안상 민감한 필드가 의도치 않게 응답에 포함될 수 있기 때문입니다. 이 엔티티 ↔ DTO 변환 과정이 처음에는 번거롭게 느껴졌지만, 시스템이 커질수록 이 분리가 얼마나 중요한지 점점 납득이 되었습니다(출처: Hibernate ORM 공식 문서).

Spring Security와 세션 vs 토큰 인증

의존성에 spring-boot-starter-security를 추가하는 순간, 기존에 아무 인증 없이 열려 있던 모든 API 엔드포인트가 갑자기 로그인 화면 뒤로 숨어버립니다. 처음 이걸 경험했을 때 꽤 당황했습니다. http://localhost:8080/ex04/test03을 바로 호출하면 됐는데 갑자기 http://localhost:8080/login으로 튕겨나갔으니까요. Spring Security가 기본적으로 모든 요청에 필터(Filter)를 걸어버리기 때문입니다.

Spring Security의 전통적인 방식은 세션(Session) 기반 인증입니다. 세션 기반 인증이란 서버가 로그인 상태 정보를 서버 메모리에 저장하고, 클라이언트는 세션 ID가 담긴 쿠키를 매 요청마다 전송하는 방식입니다. 구현이 비교적 단순하다는 장점이 있지만, 서버가 여러 대로 확장될 때 문제가 생깁니다. A 서버에서 로그인한 사용자가 B 서버로 요청을 보내면 세션 정보가 없어서 인증이 풀리는 것입니다. 이를 해결하기 위해 스티키 세션이나 세션 클러스터링 같은 방식을 쓰기도 하지만, 이 역시 관리 비용이 늘어납니다.

그래서 요즘은 토큰(Token) 기반 인증이 훨씬 많이 쓰입니다. 토큰 기반 인증이란 서버가 상태를 저장하지 않고, 로그인 성공 시 서명된 토큰을 클라이언트에게 발급하여 이후 요청에서 클라이언트가 직접 토큰을 제출하는 방식입니다. 서버 입장에서는 토큰의 서명만 검증하면 되기 때문에 서버가 몇 대로 늘어도 인증 흐름이 동일하게 유지됩니다.

Spring Security에서 사용자 인증 로직을 커스터마이징하려면 UserDetailsService 인터페이스를 구현해야 합니다. loadUserByUsername 메서드를 오버라이드해서 메모리에 하드코딩된 사용자 정보 대신 DB에서 실제 사용자를 조회하도록 바꾸는 것입니다. 이 구조가 처음에는 복잡해 보였는데, 이미 만들어진 인터페이스를 내 방식대로 구현하는 패턴에 익숙해지고 나니 오히려 확장이 쉽다는 게 느껴졌습니다. 제 경험상 이런 식으로 기존 코드를 수정해서 원하는 동작을 끼워 넣는 작업이 처음부터 만드는 것보다 더 어렵다는 말이 왜 나오는지도 이때 처음 실감했습니다.

Spring Boot는 처음 접할 때 "이게 왜 되는 거지?"라는 의문이 연속으로 나오는 프레임워크입니다. 저도 그랬고, 솔직히 지금도 완전히 이해했다고 말하기는 어렵습니다. 다만 IoC와 DI 개념을 한 번 제대로 잡아두면 나머지 동작 방식들이 훨씬 빠르게 납득됩니다. 지금 Spring Boot를 처음 시작하는 분이라면, 코드가 왜 동작하는지 하나씩 추적하기보다 어떤 계층이 어떤 역할을 하는지 구조를 먼저 파악하는 쪽이 훨씬 효율적입니다. 그 구조가 보이기 시작하면, 나머지는 생각보다 빠르게 따라옵니다.


참고: https://dltldnr2563.tistory.com/entry/%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%8020250805-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%B4%EB%82%B4%EA%B8%B0-%EB%B0%9B%EA%B8%B0-SQL%EB%A1%9C-CRUD-JPA%EC%9E%90%EB%8F%99-sql-%ED%8B%80-%EC%98%88%EC%8B%9C-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EC%A1%B4-%EC%84%B8%EC%85%98%EC%9D%84-%ED%86%A0%ED%81%B0


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 자동식단생성 연관 블로그