오늘부터 애기를 재우고 1시간씩 사이드 프로젝트를 해보겠습니다..
.\mvnw spring-boot:run
1. .\
현재 폴더(Current Directory)를 의미한다.
즉,
"현재 프로젝트 안에 있는 파일을 실행해."
라는 뜻이다.
2. mvnw
아까 네 프로젝트에 생긴 이 파일 기억나지?
mvnw
mvnw.cmd
이게 Maven Wrapper다.
원래는 컴퓨터에 Maven을 설치해야 하지만,
Spring Boot 프로젝트를 만들면 프로젝트 안에 Maven이 같이 들어온다.
그래서 아까
mvn -version
은 안 됐지만,
.\mvnw
는 된다.
3. spring-boot:run
Maven에게
"Spring Boot 프로젝트를 실행해."
라고 시키는 명령이다.
쉽게 말하면
.\mvnw spring-boot:run
=
"이 프로젝트를 실행해."
참고로 앞으로 자주 쓰는 명령어
.\mvnw clean
👉 이전 빌드 결과 삭제
.\mvnw test
👉 테스트 실행
.\mvnw package
👉 배포용 JAR 파일 생성
.\mvnw spring-boot:run
👉 개발 서버 실행
VS Code에서 Spring Boot 개발환경 구축 (Java + Spring Boot)
개발 환경
- IDE : VS Code
- JDK : Java 17 (17.0.12)
- Build Tool : Maven
- Framework : Spring Boot 3.x
- OS : Windows 10
1. Java 설치 확인
터미널에서 아래 명령어를 실행한다.
java -version
결과
openjdk version "17.0.12"
Java 17 이상이면 Spring Boot 3를 사용할 수 있다.
추가로 컴파일러도 확인한다.
javac -version
2. VS Code 확장 프로그램 설치
VS Code에서
Ctrl + Shift + P
를 누른다.
검색창에
Extensions: Install Extensions
또는
Ctrl + Shift + X
를 눌러 Extensions 화면으로 이동한다.
다음 확장 프로그램을 설치한다.
필수
- Extension Pack for Java (Microsoft)
- Spring Boot Extension Pack (VMware)
선택
- Maven for Java
- Lombok Annotations Support for VS Code
3. Spring Boot 프로젝트 생성
다시
Ctrl + Shift + P
입력 후
Spring Initializr: Create a Maven Project
선택한다.
설정은 아래와 같이 진행했다.
| Build Tool | Maven |
| Language | Java |
| Spring Boot | 최신 Stable |
| Group | com.takerp |
| Artifact | erp |
| Packaging | Jar |
| Java | 17 |
Dependencies는 다음만 선택했다.
- Spring Web
- MySQL Driver
- Lombok
- Spring Boot DevTools
Spring Data JPA는 선택하지 않았다.
이번 프로젝트는 SQL 중심의 ERP를 만들 예정이라 MyBatis를 사용할 계획이다.
4. 프로젝트 열기
생성이 완료되면 VS Code가 프로젝트를 자동으로 연다.
프로젝트 구조
erp
├── src
├── mvnw
├── mvnw.cmd
├── pom.xml
└── ...
5. Maven 실행
VS Code 터미널에서
.\mvnw spring-boot:run
실행한다.
명령어 설명
.\mvnw
현재 프로젝트에 포함된 Maven Wrapper 실행
spring-boot:run
Spring Boot 서버 실행
즉,
.\mvnw spring-boot:run
은
Spring Boot 서버를 실행하는 명령어이다.
6. 서버 실행 확인
브라우저에서
http://localhost:8080
접속한다.
처음에는 아래와 같은 화면이 나온다.
Whitelabel Error Page
This application has no explicit mapping for /error,
so you are seeing this as a fallback.
이 화면은 오류가 아니라 정상 동작이다.
이유는 아직 / 요청을 처리하는 Controller를 만들지 않았기 때문이다.
7. 패키지명 변경
프로젝트 생성 시 패키지가
com.takerp.erp_backend
으로 생성되었지만,
Java 패키지 네이밍 관례에 맞게
com.takerp.erp
로 변경하였다.
변경 후에는 반드시 서버를 다시 실행해야 한다.
.\mvnw spring-boot:run
Java 코드를 수정해도 서버가 자동 재시작되지 않는 경우가 있으므로 변경 후에는 서버 상태를 확인하는 습관을 들이자.
오늘 배운 내용
- Java 17 설치 여부 확인
- VS Code Java 개발환경 구축
- Spring Initializr로 프로젝트 생성
- Maven Wrapper(mvnw)의 역할
- Spring Boot 서버 실행 방법
- Whitelabel Error Page가 나타나는 이유
- Java 패키지 네이밍 규칙
- 코드 변경 후 서버 재시작의 중요성
@RestController
"이 클래스는 웹 요청을 받아서 데이터 그대로 응답하는 컨트롤러야!"
- 역할: 클래스 상단에 붙여서, 해당 클래스가 외부에서 들어오는 HTTP 요청을 처리하는 웹 컨트롤러임을 스프링에게 알립니다.
- 핵심 특징: @Controller와 @ResponseBody라는 두 어노테이션이 하나로 합쳐진 녀석입니다.
- 기존 @Controller는 HTML 파일(뷰)을 찾아 반환하지만, @RestController는 메소드가 리턴하는 값("Hello ERP!")을 HTML 페이지가 아닌 순수 데이터(문자열, JSON 등) 자체로 웹 브라우저에 직접 전달합니다.
2. @GetMapping("/")
"누군가 루트 주소(/)로 GET 요청을 보내면 이 메소드를 실행해!"
- 역할: 특정 HTTP 요청 경로(URL)와 방식을 아래 작성된 메소드(home())에 매핑(연결)해 줍니다.
- 상세 동작:
- GET 방식: 브라우저 주소창에 http://localhost:8080/을 입력하고 엔터를 치는 것과 같은 기본적인 페이지 조회/데이터 요청 방식입니다.
- "/" (경로): 도메인의 가장 기본 경로(루트 주소)를 의미합니다.
- 따라서 http://localhost:8080/으로 접속하면, 스프링이 이 어노테이션을 보고 home() 메소드를 실행시켜 화면에 "Hello ERP!"라는 문자를 띄워주게 됩니다.
근데 @Controller와 @ResponseBody를 언제 왜 합쳐서 @RestController 로 사용하게 되었을까?
제미나이한테 물어봤다.
스프링 프레임워크 개발진이 "자주 함께 쓰는 두 어노테이션을 매번 따로 적는 게 귀찮으니, 하나로 합친 메타 어노테이션(Meta-Annotation)을 만들자!" 해서 만든 게 바로 @RestController입니다.
스프링 부트 1.0(Spring Framework 4.0) 시절부터 도입된 기능인데요, 내부 구조를 보면 비밀이 바로 풀립니다.
@RestController 내부의 비밀
실제 스프링 내부의 @RestController 정의 코드를 까보면 이렇게 생겼습니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller // 1. 기존 Controller 포함
@ResponseBody // 2. ResponseBody 포함
public @interface RestController {
// ...
}
스프링 어노테이션은 다른 어노테이션 위에 또 어노테이션을 붙이는 겹치기(Meta-Annotation)가 가능합니다.
즉, @RestController를 클래스 위에 한 줄만 붙여도, 스프링은 자바의 메타데이터를 읽어 내부적으로 @Controller + @ResponseBody가 둘 다 붙어있는 것으로 똑같이 인식하는 원리입니다.
왜 이렇게 만들었을까요?
과거에는 서버가 HTML 웹페이지를 그려서 보내주는 MVC 패턴(JSP, Thymeleaf 등)이 주를 이뤘습니다.
- 과거 (웹페이지 반환 위주): 대부분 HTML을 반환해야 하므로 기본은 @Controller로 쓰고, 아주 가끔 Ajax 요청 같은 데이터 반환이 필요할 때만 메서드 위에 @ResponseBody를 얹어 썼습니다.
- 현재 (REST API / SPA / 앱 백엔드 위주): React, Vue, mobile 앱처럼 백엔드는 오직 JSON 데이터만 주고받는 형태가 표준이 되었습니다. 그러다 보니 컨트롤러 안에 있는 모든 메서드에 @ResponseBody를 도배해야 하는 상황이 생긴 거죠.
// ❌ 옛날 방식: 클래스 내 모든 메서드에 @ResponseBody를 붙여야 했음
@Controller
public class HomeController {
@GetMapping("/user")
@ResponseBody
public User getUser() { ... }
@GetMapping("/item")
@ResponseBody
public Item getItem() { ... }
}
이런 번거로움을 해결하기 위해, 클래스 단위에 @RestController만 한 번 딱 붙이면 내부의 모든 @GetMapping 메서드에 @ResponseBody가 자동으로 적용되도록 발전한 것입니다.






