1. java class 만들기
package org.example.springbootdeveloper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "Hello, world!";
}
}
- @RestContoller : 라우터
- @GetMapping :
2. 실행
2.1. 문제 발생 : 이미 사용중인 포트번호
예전에 8080포트로 무언가 실행해서 그런지 내가 기대한 화면이 안나왔다.
그래서 8080포트가 사용중인지 확인했다.
2.2. 문제 해결 : 이미 포트번호 사용중인 프로세스 강제 종료 후 재실행
cmd 창을 관리자 권한으로 실행하고
netstat -ano를 실행해서
현재 실행중인 포트들을 확인했다.
전체를 다 보는 건 힘들어서
netstat -ano | findstr 8080
으로 특정한(8080번 포트만) 포트번호만 검색했다.
이후 작업관리자에 들어가서
서비스 탭으로 들어오면 모든 프로세스들을 볼 수 있으며 프로세스의 id 인 PID 번호를 찾아서 어떤 프로세스가 8080포트를 사용하고 있는지 찾으면 된다.
아까 cmd 에서 봤던 pid가 4956 이니까 해당 프로세스를 종료하면 된다.
TASKKILL /PID 4956 /F
그냥 TASKKILL PID 만 하면 종료가 안 돼서 F 옵션으로 강제로 종료시켰다.
그리고 다시 인텔리제이를 실행하고 http://localhost:8080/test 로 접속하니 원하는 화면이 잘 나왔다.
3. 원리 파헤치기 - GET 요청
┌ 포트 번호
http://localhost:8080/test
│ └ 경로
└ 현재 사용중인 컴퓨터
- 웹브라우저== 클라이언트에서 '/test'라는 GET 요청 보냄
- localhost : ip로는 127.0.0.1임 == 컴퓨터 네트워크에서 사용하는 루트백 호스트명 == 현재 사용중인 컴퓨터를 의미
- 8080 : 스프링 부트의 포트 번호
- /test : @GetMapping이라는 애너테이션으로 메서드와 매핑할 때 스프링부트에서 설정한 경로
=> 웹 브라우저에서 요청할 주소에 맞게 코드를 작성하면 웹사이트나 웹 애플리케이션 개발 가능
4. Spring Boot 동작 순서
나는 총 2개의 클래스를 만들었다.
1) SpringBootDeveloperApplication.java (메인 클래스)
package org.example.springbootdeveloper;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
@SpringBootApplication
public class SpringBootDeveloperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDeveloperApplication.class, args);
}
}
2) TestController.java (컨트롤러가 될 클래스)
package org.example.springbootdeveloper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "Hello, world!";
}
}
1) SpringBootApplication 시작
- main 메서드가 포함된 애플리케이션 클래스가 실행 -> JVM은 해당 애플리케이션을 로드하고 실행
- 스프링부트 초기화 : 스프링 부트의 메인 클래스(보통 @SpringBootApplication으로 어노테이트된 클래스)가 실행 -> 스프링 부트는 애플리케이션 컨텍스트(ApplicationContext)를 생성
* @SpringBootApplication 애너테이션은 @ComponentScan, @EnableAutoConfiguration, @Configuration 등을 포함
=> 스프링 부트 애플리케이션의 설정과 실행을 간편하게 만들어 줌
1) @ComponentScan
: 스프링이 컴포넌트(빈)를 자동으로 검색하고, 애플리케이션 컨텍스트(ApplicationContext)에 등록하도록 해줌
- 스프링 부트는 애플리케이션이 시작될 때, @ComponentScan이 지정된 패키지와 그 하위 패키지들을 스캔하여 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스를 찾아 자동으로 빈으로 등록
=> 개발자가 일일이 빈을 수동으로 등록하지 않아도, 필요한 빈들이 자동으로 관리
2) @EnableAutoConfiguration
: 스프링 부트의 "자동 설정" 기능을 활성화
- 스프링 부트가 클래스패스(class path)에 있는 라이브러리와 설정 파일을 분석하여, 애플리케이션에 필요한 빈들을 자동으로 설정하고 주입하도록 함
- 스프링 부트는 애플리케이션을 실행할 때 필요한 설정들을 자동으로 구성해줌
ex. 데이터베이스 연결 설정, 웹 서버 설정 등을 자동으로 처리
=> 개발자가 복잡한 설정을 직접 할 필요X
3) @Configuration
- 해당 클래스가 스프링의 설정 파일로 사용된다는 것을 나타냄
- 스프링에서 @Configuration 어노테이션이 붙은 클래스는 자바 기반의 설정 파일로 사용됨
- 이 클래스 안에서는 여러 @Bean 메서드를 정의하여, 직접 빈을 등록하거나 설정할 수 있음
- 스프링 부트 애플리케이션의 진입점( , 보통 @SpringBootApplication 어노테이션이 붙은 클래스 -> main() 메서드 포함)에 @Configuration이 포함됨으로써, 애플리케이션 전체에 필요한 설정을 이 클래스에서 정의할 수 있게 됨
- 자동 설정 및 빈 생성 : 스프링 부트는 애플리케이션의 설정을 자동으로 구성(자동 설정) -> 필요한 빈(Bean)을 생성 및 초기화 ( 내장된 웹 서버(Tomcat, Jetty 등)와 서블릿 컨테이너가 포함)
2) 내장 서버 (Tomcat) 시작
- 서블릿 컨테이너 초기화: 스프링 부트는 내장된 서블릿 컨테이너(Tomcat, Jetty 등)를 시작 / HTTP 요청을 처리할 준비
- DispatcherServlet 설정: 스프링의 핵심인 DispatcherServlet이 초기화됨 ( DispatcherServlet : 모든 HTTP 요청을 중앙에서 처리하고, 알맞은 컨트롤러에 전달하는 역할)
1) 서블릿 컨테이너
: 자바 웹 애플리케이션을 실행하고 HTTP 요청을 처리하는 역할을 하는 서버 컴포넌트
ex. Tomcat
- 웹 서버의 일종
- HTTP 요청을 받아서 이를 처리하고, 그에 대한 응답을 생성해 클라이언트(브라우저 등)로 돌려보내는 일을 함
- 스프링 부트에서는 Tomcat 같은 서블릿 컨테이너가 내장, 별도의 설정 없이도 웹 애플리케이션을 쉽게 실행 가능
2) DispatcherServlet
- 스프링 MVC 애플리케이션의 중심에 있는 서블릿
- 모든 HTTP 요청은 우선 DispatcherServlet을 거쳐 감
- 역할 : HTTP 요청을 받아서, 이를 적절한 스프링 컨트롤러로 전달 -> 컨트롤러가 처리한 결과를 다시 클라이언트에게 돌려줌
3) 서블릿 컨테이너와 DispatcherServlet의 상호작용
3-1) HTTP 요청 수신
- 사용자가 브라우저에서 http://localhost:8080/test 같은 URL로 요청을 보냄
- 이 요청은 먼저 스프링 부트의 내장 서블릿 컨테이너(예: Tomcat)로 전달됨
- 서블릿 컨테이너는 이 요청을 수신하고, 이를 처리할 준비를 함
3-2) DispatcherServlet으로 요청 전달
- 서블릿 컨테이너는 애플리케이션 내에서 특정 서블릿이 요청을 처리하도록 설정되어 있음 => 스프링 부트에서는 이 서블릿이 바로 DispatcherServlet
- 서블릿 컨테이너는 수신한 HTTP 요청을 DispatcherServlet에게 전달
- DispatcherServlet은 요청을 받아서 어떤 컨트롤러가 이 요청을 처리할지 결정
3-3) 컨트롤러로 요청 전달
- DispatcherServlet은 요청 URL을 분석-> 어느 컨트롤러가 이 요청을 처리할지 찾음
ex. /test 요청이 들어옴 -> DispatcherServlet은 이 URL과 매핑된 컨트롤러 메서드(이 경우 TestController의 test() 메서드)를 찾음
- DispatcherServlet은 해당 컨트롤러의 메서드를 호출
- 이 과정에서 필요한 데이터를 전달하거나, 컨트롤러에서 처리 결과를 받아오는 역할도 함
3-4) 응답 생성 및 반환
- 컨트롤러 메서드가 실행되어, "Hello, world!"와 같은 결과를 반환하면, DispatcherServlet은 이 결과를 HTTP 응답으로 만들어서 다시 서블릿 컨테이너에 전달
- 서블릿 컨테이너는 이 응답을 클라이언트(브라우저)에게 전송
- 브라우저 화면에 결과가 표시됨
4) 요약
- 서블릿 컨테이너(Tomcat): HTTP 요청을 받아 DispatcherServlet에 전달하는 역할
- DispatcherServlet: 서블릿 컨테이너로부터 요청을 받아, 적절한 컨트롤러로 전달, 컨트롤러의 응답을 클라이언트에게 전달
=> 서블릿 컨테이너는 요청을 수신하고 처리할 수 있는 환경을 제공 + DispatcherServlet은 그 요청을 적절히 처리하여 응답을 생성하는 핵심 역할
=> HTTP 요청을 효율적으로 처리
3) 컨트롤러 매핑
- 스프링 부트는 @RestController로 어노테이트된 클래스(예: TestController)를 스캔하여 빈으로 등록
- 요청 매핑 설정: @GetMapping("/test")과 같은 어노테이션을 통해 요청 URL과 이를 처리할 메서드가 매핑됨( 여기서는 /test URL이 test() 메서드와 연결)
<요청 매핑 설정의 중요성>
- 요청 매핑 : 웹 애플리케이션이 어떤 URL로 들어오는 요청을 어떻게 처리할지를 결정하는 과정
1) URL과 메서드의 연결
- 특정 URL과 이를 처리할 메서드를 연결
ex. /test라는 URL로 요청 들어옴 -> 스프링 부트는 이 요청을 처리할 메서드를 찾아내고 해당 메서드를 실행
- @GetMapping, @PostMapping과 같은 어노테이션이 사용 => 요청이 들어올 때, 어떤 메서드가 호출될지를 결정하는 역할
1-1) @GetMapping
- HTTP GET 요청에 사용
- GET 요청은 주로 서버에서 데이터를 조회할 때 사용
- GET 요청은 URL에 쿼리 파라미터를 포함 가능(ex. /users?id=1)
- GET 요청은 안전하고(idempotent), 같은 요청을 여러 번 보내더라도 서버의 상태를 변경X (ex. 상품 목록을 여러 번 조회해도 상품 데이터가 변하지 않음)
1-2) @PostMapping
- HTTP POST 요청에 사용
- 서버에 데이터를 전송하거나, 서버의 상태를 변경하는 작업을 수행할 때 사용
- 데이터를 생성하거나 변경할 때:
ex.사용자가 웹 폼을 통해 새로운 데이터를 제출하거나,
새로운 리소스 (ex. 새로운 사용자 계정, 새로운 게시물 등)를 생성할 때 POST 요청을 사용
- POST 요청은 보통 요청 본문(Body)에 데이터를 포함하여 서버로 전송
(ex. JSON, XML, 폼 데이터 등).
- POST 요청은 안전하지 않으며(non-idempotent), 같은 요청을 여러 번 보내면 서버의 상태가 달라질 수있음.
(ex. 같은 데이터를 여러 번 제출하면 중복된 데이터가 생성될 수 있음)
2) 각 URL에 맞는 작업 수행
3) 애플리케이션의 구조와 유지보수
- 요청 매핑을 통해 애플리케이션의 구조를 명확하게 정리 가능
- 모든 요청이 어떤 컨트롤러에서 처리되는지를 명확히 정의 가능 -> 코드의 가독성이 높아지고 유지보수도 쉬워짐
- if 요청 매핑이 없거나 잘못 설정 -> 특정 요청이 어떻게 처리될지 예측하기 어려워지고, 이는 애플리케이션의 혼란과 오류로 이어질 수 있임
4) HTTP 요청 처리
- HTTP 요청 수신 : 사용자가 브라우저에서 http://localhost:8080/test와 같은 URL로 요청을 보냄 -> 이 요청은 스프링 부트의 내장 서버(Tomcat)에서 먼저 수신됨
- DispatcherServlet 요청 전달 : 서블릿 컨테이너는 이 HTTP 요청을 -> DispatcherServlet으로 전달
- 요청 핸들링 : DispatcherServlet은 요청 URL에 매핑된 컨트롤러 메서드(이 경우 TestController의 test() 메서드)를 호출
< DispatcherServlet의 요청 처리 과정>
- DispatcherServlet은 스프링 MVC의 핵심 컴포넌트
- 모든 HTTP 요청을 받아서 적절한 핸들러(Controller 메서드)를 찾아 호출하는 역할
1) 요청 수신
- 브라우저나 클라이언트가 서버로 HTTP 요청을 보냄
- 이 요청은 먼저 서블릿 컨테이너(Tomcat 등)에 도착
- 서블릿 컨테이너는 이 요청을 DispatcherServlet으로 전달
2) 핸들러 매핑 찾기
- DispatcherServlet은 요청된 URL과 HTTP 메서드(GET, POST 등)를 기반으로 적절한 핸들러를 찾아야 함
- DispatcherServlet은 HandlerMapping이라는 인터페이스를 구현한 여러 객체들을 순차적으로 조회( 이 객체들은 요청을 처리할 핸들러를 찾는 역할)
2-1) ex 1. RequestMappingHandlerMapping
- 현대적인 스프링 MVC의 기본 요청 매핑 방식.
- HandlerMapping은 @RequestMapping, @GetMapping, @PostMapping 등 요청 매핑 어노테이션이 붙어 있는 메서드를 찾아내어, 해당 메서드를 HTTP 요청과 매핑
- RequestMappingHandlerMapping 은 요청된 URL과 HTTP 메서드(GET, POST 등)를 기준으로 적절한 컨트롤러 메서드를 찾음
- 복잡한 URL 매핑과 다양한 HTTP 메서드 지원
<동작방식>
- 스프링 부트 애플리케이션이 시작될 때, RequestMappingHandlerMapping은 @Controller 또는 @RestController가 붙은 클래스들을 스캔
- @RequestMapping이나 그 파생 어노테이션(@GetMapping, @PostMapping 등)이 붙은 메서드를 찾고, 해당 메서드들을 매핑 테이블에 등록
- 이후 HTTP 요청이 들어오면, RequestMappingHandlerMapping은 요청된 URL과 메서드를 분석하여, 매핑 테이블에 등록된 메서드 중에서 해당 요청을 처리할 수 있는 메서드를 찾아냄
- ex. /test URL에 대한 GET 요청이 들어오면, RequestMappingHandlerMapping은 @GetMapping("/test") 어노테이션이 붙은 메서드를 찾아 실행하도록 설정
2-2) ex2. BeanNameUrlHandlerMapping
- 과거에 자주 사용된 매핑 방식
- URL 패턴과 스프링 빈 이름을 기준으로 핸들러를 찾음 (매핑)
- 주로 특정 URL 패턴에 맞는 스프링 빈을 직접 매핑할 때 사용
- 주로 XML 설정 파일이나 자바 기반 설정에서 스프링 빈의 이름을 직접 설정하고, 이를 URL과 매핑하는 방식으로 사용
<동작 방식>
- 스프링 애플리케이션이 시작될 때, BeanNameUrlHandlerMapping은 애플리케이션 컨텍스트에 등록된 모든 빈을 스캔
- 각 빈의 이름이 특정 URL 패턴과 일치 -> 그 빈을 해당 URL의 요청을 처리할 핸들러로 사용
- 주로 간단한 URL 패턴 매핑에 사용
- RESTful 스타일보다는 기존의 서블릿 기반 웹 애플리케이션에서 더 자주 사용되었음
ex. <bean name="/myHandler" class="com.example.MyHandler"/>
=> /myHandler라는 URL로 들어오는 요청을 com.example.MyHandler 클래스의 빈으로 처리
-> 이 빈은 URL에 매핑되어 있으며, 요청을 처리하는 메서드를 가지고 있어야 함
=> 각 HandlerMapping은 요청 URL과 일치하는 핸들러를 가지고 있는지 확인하며, 일치하는 핸들러를 찾으면 그 핸들러를 반환
5) 컨트롤러 메서드 실행
- 메서드 실행 및 응답 생성: TestController의 test() 메서드가 호출되어 "Hello, world!"라는 문자열을 반환
- 응답 반환: 이 반환된 문자열은 HTTP 응답의 본문(Body)으로 설정됨
6) HTTP 응답 전송
- HTTP 응답 생성: 스프링 부트는 반환된 데이터를 기반으로 HTTP 응답을 생성 -> 이를 클라이언트(브라우저)로 전송
- 브라우저에서 응답 표시: 클라이언트 측에서는 브라우저가 이 응답을 받아 화면에 "Hello, world!"라는 내용을 표시
<브라우저의 역할: 응답을 받아 페이지 렌더링하기>
1) HTTP 응답 수신
- 서버가 클라이언트(브라우저)로 HTTP 응답을 보내면, 브라우저는 이 응답을 수신
- 이 응답에는 주로 HTTP 상태 코드(ex. 200 OK, 404 Not Found), 헤더(메타데이터), 그리고 본문(Body)이 포함
2) HTTP 응답 해석
- 브라우저는 응답의 상태 코드, 헤더, 그리고 본문을 분석
- 상태 코드는 요청이 성공적으로 처리되었는지, 아니면 오류가 발생했는지를 나타냄
ex. 200 OK : 요청이 성공적으로 처리되었음
3) HTML 파싱 및 DOM 트리 생성
ex.
- if 응답 본문이 HTML 문서 -> 브라우저는 이를 파싱하여 DOM(Document Object Model) 트리를 생성
- HTML 문서의 각 요소(예: <div>, <p>, <img> 등)는 DOM 트리의 노드로 변환
- DOM 트리는 웹페이지의 구조와 내용을 나타내며, 자바스크립트와 같은 스크립트 언어로 조작 가능
4) CSS 파싱 및 스타일링
- 브라우저는 HTML 문서 내에서 CSS(스타일시트) 파일이나 스타일 태그를 찾아 이를 파싱
- 파싱된 CSS는 DOM 트리와 결합하여 렌더 트리를 생성
- 렌더 트리는 각 요소가 화면에 어떻게 표시될지(색상, 크기, 위치 등)를 결정
- 스타일이 적용된 후, 웹페이지의 레이아웃이 계산됨
5) 자바스크립트 실행
- 브라우저는 HTML 문서에서 자바스크립트 코드를 찾아 실행
- 자바스크립트는 웹페이지의 동작을 제어하거나, DOM 트리를 조작가능
- 자바스크립트에 의해 DOM이 변경되거나, 새로운 콘텐츠가 동적으로 로드 가능
6) 이미지 및 기타 리소스 로딩
- 브라우저는 HTML 문서에 포함된 이미지, 폰트, 비디오 등의 외부 리소스를 요청하여 로드
- 이 과정은 비동기적으로 이루어지며, 리소스가 로드되는 동안에도 페이지의 다른 부분은 계속해서 렌더링됨
7) 화면 렌더링
- 브라우저는 모든 리소스가 로드되고, DOM 트리와 렌더 트리가 완성되면, 이를 기반으로 화면에 최종적으로 웹페이지를 렌더링함
=> 사용자는 브라우저를 통해 웹페이지를 볼 수 있게 됨
=> 스프링 부트는 많은 자동 설정과 내부적인 처리 과정을 거쳐 개발자가 최소한의 코드만 작성해도 애플리케이션이 작동하도록 해줌
=> 이로 인해 복잡한 설정 없이도 RESTful 웹 서비스를 빠르게 구현할 수 있음
아직 스프링 부트의 내부 구조파트로 넘어오지 않아서 모든 내용을 다 이해할 수는 없지만 뒷부분을 공부하고 다시 이 파트로 돌아온다면 이해도가 높아질 것 같다
'Spring Boot' 카테고리의 다른 글
[Spring Boot] 6. 스프링 부트 자동 구성 (0) | 2024.08.19 |
---|---|
[Spring Boot] 5. 스프링 부트 스타터 (Spring Boot Starter)살펴보기 (0) | 2024.08.19 |
[Spring Boot] 3. 스프링(Spring)과 스프링부트(Spring Boot) (0) | 2024.08.19 |
[Spring Boot] 2.5.개념 - 백엔드 개발자의 업무 / 백엔드 프로그래밍 언어 / 자바 애너테이션(Annotation, @) (0) | 2024.08.19 |
[Spring Boot] 2.4.개념 - 라이브러리(library)와 프레임워크(framework) (0) | 2024.08.19 |