자바 백엔드 입문 14편 — 첫 Bean 만들기 Hello Spring

2026-05-16자바 백엔드 입문

자바 백엔드 입문 14편. 빈 Spring Boot 프로젝트에 컨트롤러 한 개를 추가해 브라우저에 'Hello, Spring' 응답을 띄우는 가장 손에 잡히는 첫 코드. Bean이 컨테이너의 직원이라는 비유로 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 14편 — 첫 Bean 만들기 Hello Spring

이 글은 자바 백엔드 입문 시리즈 59편 중 14편이에요. 12편에서 만든 빈 Spring Boot 프로젝트에 컨트롤러 클래스 한 개를 추가해 브라우저에 "Hello, Spring" 응답을 띄워볼게요. Phase 1의 마지막 글이고, Phase 2 IoC 컨테이너로 넘어가는 다리예요. 여기서 처음으로 Bean이라는 단어를 손에 잡히는 형태로 만납니다.

첫 Bean을 짤 때 어렵게 느껴지는 이유

7편에서 콘솔에 "Tomcat started" 띄우고 끝났는데, 브라우저에서 응답을 받으려면 "컨트롤러" 라는 게 필요해요. 처음 마주하면 두 가지가 어색합니다.

첫째, @RestController·@GetMapping 같은 어노테이션이 마법처럼 동작해요. "이게 어떻게 알아서 HTTP 요청을 받지?" 가 안 잡혀요.

둘째, Bean이라는 단어가 처음 등장합니다. 콩? 자바 빈? 헷갈리는 이름이에요. "Bean이랑 일반 객체가 어떻게 다른가" 가 안 보입니다.

이 글에서는 Bean = Spring 컨테이너의 직원 비유로 풀어 갑니다. 끝까지 따라하면 브라우저에서 첫 응답을 받게 되고, "Bean이 도대체 뭔지" 가 머리에 박혀요.

컨트롤러 클래스 한 개 추가하기

7편에서 만든 프로젝트 (my-shop) 의 메인 클래스 옆에 새 파일을 만들어요.

src/main/java/com/example/myshop/HelloController.java:

package com.example.myshop;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring";
    }
}

저장하면 — DevTools가 자동으로 재시작해줘요 (콘솔에 "Restarting..." 보임). 브라우저에서 http://localhost:8080/hello 접속하면 "Hello, Spring" 이 떠요. 첫 백엔드 API 완성.

10줄짜리 코드에 마법이 다 들어 있어요. 하나씩 풀어볼게요.

@RestController와 @GetMapping 풀이

@RestController 는 두 어노테이션을 합친 거예요.

  • @Controller"이 클래스로 객체 한 개 만들어 컨테이너에 등록해 줘 + HTTP 요청 처리 컨트롤러로 인식해 줘"
  • @ResponseBody"이 메서드가 반환하는 값은 HTTP 응답 본문으로 그대로 보내 줘"

평범한 @Controller 만 쓰면 "뷰 템플릿(HTML)을 렌더해서 반환" 이 기본 동작이에요. REST API처럼 "JSON·문자열을 그대로 응답으로 보내고 싶다"@RestController 가 정답. 현대 백엔드의 80%가 이 패턴이에요.

@GetMapping("/hello")"HTTP GET 요청이 /hello 경로로 오면 이 메서드를 실행해 줘" 라는 메모예요. 비슷한 친구들이 있어요.

어노테이션HTTP 메서드용도
@GetMappingGET데이터 조회
@PostMappingPOST데이터 생성
@PutMappingPUT데이터 전체 수정
@PatchMappingPATCH데이터 부분 수정
@DeleteMappingDELETE데이터 삭제

REST API의 기본 5종 세트. 19~23편 Web MVC에서 깊이 다뤄요.

Bean이 도대체 무엇인가 — 컨테이너의 직원

여기서 처음 등장하는 핵심 단어 Bean. 자바 빈(JavaBean)이라는 단어가 1990년대부터 있었고, Spring이 그 개념을 가져와 자기 식대로 재정의했어요.

Spring Bean = "Spring 컨테이너가 직접 만들고 관리하는 객체". 일반 객체와 결정적으로 다른 점이 하나 있어요 — 객체 생성을 우리가 안 하고 Spring이 해줘요. new HelloController() 같은 코드를 우리가 안 짭니다. @RestController 어노테이션이 박혀 있으면 Spring이 시작될 때 "아, 이거 Bean이구나" 하고 알아서 객체를 한 번 찍어내 컨테이너에 보관해요.

회사 비유로 풀어볼게요. Spring 컨테이너 = 회사 인사팀, Bean = 회사가 정식 채용해 인사팀이 관리하는 직원. 회사가 채용 절차를 통해 직원 한 명을 고용하면, 그 직원은 회사 어디서든 "필요할 때마다 호출되는" 자원이 돼요. 다른 부서에서 "개발팀의 김 과장 좀 빌려주세요" 하면 인사팀이 김 과장을 보내주는 그림이에요.

자바 코드에서도 똑같아요. @RestController 가 박힌 HelloController 클래스는 Spring 시작 시점에 객체 한 개로 찍혀 컨테이너에 보관돼요. 누군가 "GET /hello 요청 처리할 객체 좀 줘" 하면 컨테이너가 그 보관해둔 객체를 꺼내서 메서드를 호출해주는 거예요.

💡 일반 객체 vs Bean

평범한 자바 객체 = 우리가 new로 만들어 우리가 관리. Spring Bean = Spring이 만들고 Spring이 관리. "누가 생명주기를 책임지나" 가 결정적 차이예요.

Spring이 시작될 때 일어나는 일

SpringApplication.run() 한 줄 호출에서 다음 5단계가 일어나요.

  1. Spring 컨테이너 생성ApplicationContext 라는 이름의 거대한 객체 보관소가 만들어짐
  2. 컴포넌트 스캔@SpringBootApplication 이 박힌 메인 클래스 패키지부터 시작해 모든 클래스 검사
  3. Bean 인식@RestController·@Service·@Component·@Repository 어노테이션 박힌 클래스 발견
  4. Bean 생성·등록 — 발견한 클래스로 객체 한 개씩 찍어내 컨테이너에 보관
  5. 내장 Tomcat 시동 — HTTP 서버 띄우고 8080 포트 열어 요청 대기

이 흐름에서 HelloController 는 3·4단계에서 만들어져 컨테이너에 보관돼요. 그 후 브라우저에서 /hello 요청이 들어오면 — Tomcat이 받음 → Spring DispatcherServlet이 처리 → @GetMapping("/hello") 박힌 메서드 찾음 → 컨테이너에서 HelloController Bean 꺼내 메서드 호출 → 반환값을 HTTP 응답으로 → 브라우저에 "Hello, Spring" 표시.

여기서 시험 함정이 하나 있어요. "우리가 직접 new HelloController() 를 해서 사용한다" 가 보기로 나오면 X. Spring Boot에서는 컨트롤러를 직접 new 하지 않습니다. Spring 컨테이너가 알아서 한 번 만들고 모든 요청에서 그 같은 객체를 재사용해요. 이 "한 번 만들어 계속 재사용" 이 Spring Bean의 기본 동작이고, Singleton Scope라고 불러요. 14편에서 다시 깊이 다뤄요.

DevTools가 도와주는 핫리로드

7편에서 추가한 Spring Boot DevTools 가 여기서 빛을 발해요. 코드를 한 줄 수정하고 저장하면, DevTools가 자동으로 다음을 처리해줍니다.

  1. 변경된 .class 파일 감지
  2. Spring 컨테이너를 빠르게 재시작 (전체 JVM 재시작이 아니라 "애플리케이션 클래스 로더만" 교체 — 빠름)
  3. 콘솔에 "Restarting..." 출력
  4. 브라우저는 그대로, 다음 요청부터 새 코드 적용

서버를 직접 껐다 켤 필요가 없어요. 코드 짜고 바로 브라우저에서 결과 확인. 개발 속도 차원이 다릅니다.

한 줄 정리 — @RestController + @GetMapping 한 메서드 + 브라우저 접속 = 첫 백엔드 API. Bean = Spring 컨테이너가 직접 만들고 관리하는 객체.

시험 직전 한 번 더 — 첫 Bean 입문자가 매번 헷갈리는 것

  • 첫 컨트롤러 = @RestController + @GetMapping("/경로") 박힌 메서드
  • @RestController = @Controller + @ResponseBody 합친 어노테이션
  • @Controller 단독 = 뷰 템플릿(HTML) 렌더 기본, @RestController = 응답 본문 직접 반환
  • HTTP 메서드 매핑 = @GetMapping·@PostMapping·@PutMapping·@PatchMapping·@DeleteMapping
  • Bean = Spring 컨테이너가 직접 만들고 관리하는 객체
  • 일반 객체 = 우리가 new로 생성·관리, Bean = Spring이 생성·관리
  • @RestController·@Service·@Component·@Repository 모두 "이 클래스로 Bean 한 개 만들어" 메모
  • Spring 시작 5단계 = 컨테이너 생성 → 컴포넌트 스캔 → Bean 인식 → Bean 등록 → Tomcat 시동
  • ApplicationContext = Spring 컨테이너의 정식 이름. 거대한 객체 보관소
  • 컨트롤러는 우리가 new 안 함 — Spring이 자동 생성
  • Spring Bean 기본 = Singleton Scope — 한 번 만들어 모든 요청에서 재사용
  • Singleton이라 컨트롤러 안의 인스턴스 변수는 멀티스레드 환경에서 위험 (14편에서 깊이)
  • DispatcherServlet = 모든 HTTP 요청의 진입점. 적절한 컨트롤러 찾아 호출
  • 컴포넌트 스캔 시작점 = @SpringBootApplication 박힌 메인 클래스의 패키지
  • 메인 클래스 패키지보다 "위" 또는 "옆" 패키지에 박힌 클래스는 스캔 X (자주 막히는 함정)
  • DevTools = 코드 수정 시 자동 재시작 (전체 JVM 아니라 클래스 로더만 교체)
  • 브라우저 새로고침만 누르면 새 코드 결과 확인 가능
  • 기본 포트 8080 — application.properties 에서 server.port=8090 같이 변경 가능
  • application.properties 또는 application.yml = Spring Boot 설정 파일
  • "왜 @RestController 클래스 안의 인스턴스 변수가 위험한가" 면접 단골 — Singleton 공유 + 멀티스레드

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!