JavaRush /Java Blog /Random-KO /Hello World에서 Spring Web MVC까지 그리고 서블릿은 그것과 어떤 관련이 있나요?
Viacheslav
레벨 3

Hello World에서 Spring Web MVC까지 그리고 서블릿은 그것과 어떤 관련이 있나요?

Random-KO 그룹에 게시되었습니다
Hello World에서 Spring Web MVC까지 그리고 서블릿은 그것과 어떤 관련이 있나요? - 1

소개

우리가 알고 있듯이 Java의 성공은 바로 네트워크 연결을 위해 노력하는 소프트웨어의 발전 덕분입니다. 따라서 일반적인 콘솔 애플리케이션인 “ Hello World ”를 기본으로 콘솔 애플리케이션에서 네트워크 애플리케이션이 되기 위해 필요한 것이 무엇인지 이해하겠습니다. 따라서 먼저 Java 프로젝트를 생성해야 합니다. 프로그래머는 게으른 사람들이다. 선사 시대에는 일부가 매머드를 사냥하고 있을 때 다른 일부는 앉아서 다양한 Java 라이브러리와 디렉토리 구조를 혼동하지 않으려고 노력했습니다. 개발자가 애플리케이션 생성 프로세스를 제어할 수 있도록 "나는 이러한 버전 2의 라이브러리를 원합니다"라고 간단히 작성할 수 있도록 시스템 구축이라는 특별한 도구를 생각해 냈습니다. 가장 유명한 두 가지는 MavenGradle 입니다 . 이 글에서는 Gradle을 사용하겠습니다. 이전에는 디렉토리 구조를 직접 생성해야 했다면 이제 Gradle Init 플러그인을gradle init --type java-application 사용하여 Gradle을 사용하면 하나의 명령 으로 디렉토리 구조와 기본 Main 클래스가 포함된 Java 프로젝트를 생성할 수 있습니다. Hello World 콘솔이 있는 Java 애플리케이션(java-application )을 만듭니다. 완료되면 build.gradle 디렉토리에 파일이 나타납니다 . 이것은 우리의 빌드 스크립트 입니다. 즉, 이를 위해 수행해야 하는 작업에 대한 설명과 함께 애플리케이션을 생성하기 위한 특정 스크립트입니다. 이를 열고 다음 줄을 추가해 보겠습니다. jar.baseName = 'webproject' Gradle을 사용하면 프로젝트에서 다양한 작업을 수행할 수 있으며 이러한 작업을 작업 이라고 합니다 . 명령(작업)을 실행하면 /build/libsgradle build 디렉터리 에 JAR 파일이 생성됩니다 . 그리고 짐작한 대로 이름은 이제 webproject.jar 이 됩니다 . 그러나 를 실행하면 오류가 발생합니다: . 이는 Java 애플리케이션의 경우 매니페스트를 첨부해야 하기 때문입니다. 이는 애플리케이션 작업 방법, 애플리케이션 인식 방법에 대한 설명입니다. 그러면 Java 애플리케이션을 실행할 JVM은 어떤 클래스가 프로그램의 진입점인지 및 기타 정보(예: 클래스 경로)를 알게 됩니다. 빌드 스크립트의 내용을 자세히 살펴보면 플러그인이 연결되어 있는 것을 볼 수 있습니다. 예: Gradle Java 플러그인 페이지 로 이동하면 매니페스트를 구성할 수 있음을 확인할 수 있습니다. java -jar ./build/libs/webproject.jarno main manifest attributeapply plugin: 'java'
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
프로그램의 진입점인 메인 클래스는 Gradle Init Plugin에 의해 생성되었습니다. 그리고 이는 mainClassName 매개변수에도 지정됩니다. 그러나 이것은 우리에게 적합하지 않았습니다. 왜냐하면 ... 이 설정은 다른 플러그인인 Gradle Application Plugin 을 나타냅니다 . 그래서 화면에 Hello World를 표시하는 Java 애플리케이션이 있습니다. 이 Java 애플리케이션은 JAR(Java ARchive)로 패키지되어 있습니다. 단순하고 콘솔 기반이며 최신이 아닙니다. 웹 애플리케이션으로 전환하는 방법은 무엇입니까?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

서블릿 API

Java가 네트워크와 함께 작동할 수 있도록 고대에는 Servlet API 라는 사양이 나타났습니다 . 클라이언트(예: 브라우저)로부터 메시지를 수신하고 응답(예: 페이지 텍스트 포함)을 보내는 클라이언트-서버 상호 작용을 설명하는 것이 이 사양입니다. 물론 그 이후로 많은 변화가 있었지만, 중요한 점은 자바 애플리케이션이 웹 애플리케이션이 되기 위해서는 서블릿 API를 사용한다는 점이다. 근거 없는 추측을 피하기 위해 JSR-000340 JavaTM Servlet 3.1 사양을 선택해 보겠습니다 . 우선, " 1장: 개요 "에 관심이 있습니다. 우리가 이해해야 할 기본 개념을 설명합니다. 먼저, 서블릿이란 무엇인가? " 1.1 서블릿이란? " 장에서는 서블릿이 컨테이너에 의해 관리되고 동적 콘텐츠를 생성하는 Java 구성 요소라고 설명합니다 . 다른 Java 구성요소와 마찬가지로 서블릿은 바이트코드로 컴파일되고 Java 기술을 사용하여 웹 서버에 로드될 수 있는 Java 클래스입니다. 서블릿 컨테이너에 의해 구현되는 요청/응답 패러다임의 프레임워크 내에서 서블릿이 웹 클라이언트(예: 브라우저)와 상호 작용하는 것이 중요합니다. 서블릿은 일종의 서블릿 컨테이너에 존재하는 것으로 나타났습니다. 이게 뭔가요? " 1.2 서블릿 컨테이너란 무엇입니까? " 장에서 서블릿 컨테이너는 요청이 전송되고 응답이 전송되는 네트워크 서비스를 제공하는 웹 서버 또는 애플리케이션 서버의 일부라고 설명합니다 . 바로 이 서블릿 컨테이너가 서블릿의 수명주기를 관리합니다. 모든 서블릿 컨테이너는 최소한 HTTP 프로토콜을 지원해야 하지만 다른 프로토콜도 지원할 수 있습니다. 예를 들어 HTTPS입니다. 서블릿 컨테이너가 서블릿이 실행되는 환경에 보안 관련 제한 사항을 적용할 수 있다는 점도 중요합니다. 또한 “ 10.6 웹 애플리케이션 아카이브 파일 ” 에 따라 웹 애플리케이션을 WAR(Web ARchive) 파일로 패키징해야 하는 것도 중요합니다. 즉, 이제 다른 것을 위해 jar 및 애플리케이션 플러그인을 제거해야 합니다. 이것이 Gradle WAR 플러그인 입니다 . 그리고 jar.baseName 대신 war.baseName을 지정하십시오. 왜냐하면 더 이상 jar 플러그인을 사용하지 않으므로 매니페스트 설정도 제거했습니다. JAR을 시작할 때 JVM(Java Virtual Machine)은 매니페스트를 통해 애플리케이션 작업 방법을 알려줘야 했습니다. JVM이 실행 중이었기 때문입니다. 분명히 웹 애플리케이션은 일종의 웹 서버에 의해 실행됩니다. 그가 우리 웹 애플리케이션으로 작업하는 방법을 그에게 어떻게든 알려줘야 한다는 것이 밝혀졌습니다. 그리고 그것은 그렇습니다. 웹 애플리케이션에는 고유한 특별한 선언문이 있습니다. 이를 배포 설명 자라고 합니다 . 전체 섹션이 " 14. 배포 설명자 " 전용입니다. 중요한 섹션이 있습니다: " 10장:". Servlet API의 관점에서 웹 애플리케이션이 무엇인지에 대해 설명합니다. 예를 들어 " 10.5 디렉터리 구조 " 장에서는 배포 설명자가 있어야 하는 위치를 표시합니다. /WEB-INF/web.xml. WEB-INF를 어디에 배치할까요? Gradle WAR 플러그인에 명시된 대로 새로운 레이아웃을 추가합니다 . src/main/webapp따라서 이러한 디렉토리를 생성하고 내부에 WEB-INF 디렉토리를 생성하고 내부에 web.xml 파일을 생성합니다. 디렉토리가 중요합니다. META-INF가 아니라 WEB-INF라고 합니다! " 14.5.1 기본 예제 " XML 예제 에서 복사해 보겠습니다 .
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
보시다시피 XML 문서가 구성에 사용됩니다. 유효한(Valid) 것으로 간주되기 위해서는 XML 문서가 일부 "스키마"를 준수해야 합니다. 이것을 XML 문서에 대한 일종의 인터페이스라고 생각할 수 있습니다. 스키마는 XML 문서에 포함될 수 있는 요소, 요소, 순서, 요구 사항 및 기타 측면을 정의할 수 있는 데이터 유형을 지정합니다. 문서에서 복사한 예는 버전 2.5를 나타내지만 우리는 버전 3.1을 사용하려고 합니다. 당연히 버전이 바뀌면서 사양이 바뀌고, 새로운 기능이 추가되었습니다. 따라서 버전 2.5(web-app_2_5.xsd)에 사용된 스키마가 아닌 다른 스키마를 사용해야 합니다. 버전 3.1에는 어떤 체계를 사용해야 합니까? 문서는 " 14.3 배포 설명자 " 장에서 우리에게 도움이 될 것입니다. 즉 specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd , 스키마에 대한 링크를 모든 곳에서 지정된 xsd로 교체해야 하며, 이를 version="2.5"3.1로 변경하는 것을 잊지 말고 모든 곳에서 네임스페이스도 변경해야 합니다( xmlns 및 xsi:schemaLocation). 이는 우리가 어떤 네임스페이스 내에서 작업할지를 나타냅니다(매우 간단하게 말하면 어떤 요소 이름을 사용할 수 있는지). 스키마 파일을 열면 targetNamespace에는 우리가 지정해야 하는 동일한 네임스페이스가 포함됩니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
우리가 기억하는 것처럼 Jar 파일의 매니페스트에서 어떤 클래스를 사용할지 작성했습니다. 여기서 무엇을 해야 할까요? 여기서는 웹 클라이언트로부터 요청을 받을 때 사용할 서블릿 클래스를 지정해야 합니다. 설명은 " 14.4 배포 설명 다이어그램 " 장에서 읽을 수 있습니다 . 다음과 같이 보일 것입니다:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
여기에서는 모든 것이 간단합니다. 서버렛이 선언된 후 특정 템플릿에 매핑됩니다. 이 경우 /app. 템플릿이 실행되면 서블릿 메소드가 실행됩니다. 아름다움을 위해서는 xml 구성을 수정하는 것을 잊지 말고 App 클래스를 패키지로 전송해야 합니다. 하지만 그게 전부는 아닙니다. 앱은 서블릿이어야 합니다. 서블릿이 된다는 것은 무엇을 의미하나요? 이는 HttpServlet 에서 상속받아야 함을 의미합니다 . " 8.1.1 @WebServlet " 장에서 예제를 볼 수 있습니다 . 이에 따르면 App 클래스는 다음과 같습니다.
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
하지만 우리 프로젝트는 아직 준비되지 않았습니다. 이제 우리는 Servlet API 버전 3.1에 의존하기 때문입니다. 이는 빌드 스크립트에서 Servlet API에 대한 종속성을 표시해야 함을 의미합니다. JVM은 코드에 작성한 내용이 정확하고 이를 사용하는 방법을 알아야 합니다. 우리가 기억하는 것처럼 사양은 본질적으로 모든 것이 어떻게 작동해야 하는지 설명하는 인터페이스일 뿐입니다. 그리고 구현은 웹 서버 측에 있습니다. 따라서 Servlet API가 없으면 Maven Central에서 필요한 라이브러리 찾기: javax.servlet-api가 있을 것입니다 . 그리고 종속성 블록 에 항목을 추가합니다 . Maven 저장소에는 보시다시피 제공됨이라고 나와 있습니다. 종속성을 사용하기 전에 범위를 지정해야 합니다. Gradle에는 "제공됨"이라는 범위가 없지만 " 컴파일 전용 " 범위가 있습니다. 그러므로 우리는 다음과 같이 말할 것입니다: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' 어, 모든 것이 괜찮은 것 같나요? Gradle Build는 프로젝트를 WAR 파일로 빌드합니다. 그리고 다음에는 무엇을 해야 할까요? 먼저 웹 서버가 필요합니다. Google에서는 “ web server java list ”라고 작성하면 웹 서버 목록이 표시됩니다. 예를 들어 TomCat 과 같이 이 목록에서 선택해 보겠습니다 . Apache Tomcat 웹사이트 로 이동하여 최신 버전(현재 버전 9)을 zip 아카이브(Windows의 경우)로 다운로드합니다. 어떤 디렉토리에 압축을 푼다. 만세, 웹 서버가 있습니다. bin 하위 디렉터리 에 있는 웹 서버 디렉터리의 명령줄에서 catalina를 실행 하고 사용 가능한 옵션을 확인합니다. 하자: catalina start. 각 웹 서버에는 웹 서버가 모니터링하는 디렉터리가 있습니다. 웹 응용 프로그램 파일이 나타나면 웹 서버가 해당 파일을 설치하기 시작합니다. 이 설치를 배포 또는 배포 라고 합니다 . 예, 그렇기 때문에 " 배포 설명자 "입니다. 즉, 애플리케이션을 올바르게 배포하는 방법입니다. Tomcat에서 이 디렉토리는 webapps 입니다 . Gradle 빌드를 사용하여 만든 전쟁을 복사해 보겠습니다. 그 후 로그에 다음과 같은 내용이 표시됩니다. Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms 더 잘 이해하기 위해 tomcat 디렉토리에서 파일을 편집하고 \conf\tomcat-users.xml다음 줄을 추가합니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
이제 서버를 다시 시작하고(catalina stop, catalina start) http://127.0.0.1:8080/manager 해당 주소로 이동하면 모든 애플리케이션의 경로가 표시됩니다. 우리 웹 프로젝트에는 /webproject 경로가 주어졌을 가능성이 높습니다. 이 길은 무엇입니까? " 10.1 웹 서버 내의 웹 응용 프로그램 " 장의 사양에서는 웹 응용 프로그램이 응용 프로그램 내의 일부 경로(이 경우 /webproject)와 연결되어 있다고 명시합니다. 이 경로를 통한 모든 요청은 동일한 ServletContext와 연결됩니다. 이 경로는 contextRoot 라고도 합니다 . 그리고 " 10.2 ServletContext와의 관계 " 에 따르면 서블릿 컨테이너는 웹 애플리케이션과 ServletContext를 일대일로 연결합니다. 즉, 각 웹 애플리케이션에는 자체 ServletContext가 있습니다. ServletContext 란 무엇입니까 ? 사양에 명시된 대로 ServletContext는 서블릿이 실행 중인 " 애플리케이션 보기"를 제공하는 객체입니다 . 서블릿 컨텍스트는 서블릿 API 사양의 4장 에 자세히 설명되어 있습니다. 놀랍게도 버전 3.1의 Servlet API에는 더 이상 web.xml이 필요하지 않습니다. 예를 들어, 주석을 사용하여 서블릿을 정의할 수 있습니다.
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
또한 " Java EE 인터뷰 - JEE 서블릿 API(질문 및 답변) " 주제에 대해 권장됩니다 . 따라서 우리에게는 서블릿이 있습니다. 웹 클라이언트에 어떤 응답을 제공할지 담당합니다. 사용자로부터 요청을 받고, 액세스된 경로를 서블릿 경로와 일치시키고, 일치하는 항목이 발견되면 서블릿을 실행하는 ServletContainer가 있습니다. 괜찮은. 이 세계 그림에서 봄은 어떤 위치를 차지하고 있습니까 ?

스프링 웹 MVC

좋습니다. 웹 애플리케이션이 있습니다. 이제 Spring을 연결해야 합니다. 어떻게 할 수 있나요? 먼저 Spring을 프로젝트에 올바르게 연결하는 방법을 알아야 합니다. 이전에는 Spring 플랫폼 프로젝트 의 문서에 따라 이 작업을 수행할 수 있었지만 지금은 “ 플랫폼은 2019년 4월 9일에 지원 수명이 종료됩니다 ”, 즉 권장되지 않습니다. 그것을 사용하세요, 왜냐면 곧 더 이상 지원되지 않습니다. 유일한 방법은 " 플랫폼 사용자는 Spring Boot의 종속성 관리를 사용하도록 권장됩니다 "입니다. 따라서 Spring Boot 문서 로 넘어가겠습니다 . Spring Boot 자체를 사용하지 않고 Spring Boot의 종속성 관리만 사용한다는 점을 분명히 하겠습니다. 즉, Spring Boot 프로젝트는 사용할 라이브러리 버전(Spring MVC 포함)에 대한 지식을 제공할 수 있습니다. 거기에서 우리는 3.2를 찾을 것입니다 . Spring Boot의 종속성 관리를 별도로 사용합니다 . 설명서에 따라 빌드 스크립트에 다음을 추가합니다.
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
그리고
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
보시다시피, 우리는 apply false다음과 같이 표시했습니다. 우리는 Spring Boot 자체를 사용하지 않지만 거기에서 종속성 관리를 사용합니다. 이러한 종속성 관리는 BOM - " Bill Of Materials " 라고도 합니다 . 이제 Spring Web MVC 프로젝트 자체를 연결할 준비가 되었습니다. Spring Web MVC는 Spring Framework 프로젝트 의 일부 이며 " Web Servlet " 섹션 에 관심이 있습니다 . 빌드 스크립트에 종속성을 추가해 보겠습니다 compile 'org.springframework:spring-webmvc'. 보시다시피 범위 컴파일을 설정했습니다. 웹 서버는 우리에게 Spring을 제공하지 않습니다. 우리 프로젝트는 자체적으로 Spring 라이브러리를 포함해야 합니다. 다음으로, " 1.2. DispatcherServlet " 섹션을 읽는 것이 중요합니다 . 여기에서는 Spring MVC가 " 프론트 컨트롤러 " 패턴 을 중심으로 구축되었다고 하며 , 여기에는 다른 구성 요소에 대한 구성 및 위임을 제공하는 일종의 중앙 서블릿이 있습니다. . Dispatcher는 Dispatcher로 번역될 수 있습니다. 따라서 우선 web.xml에서 다음을 선언합니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
보시다시피 이는 실제로 Servlet API 사양에 정의된 일반 리스너입니다. 더 정확하게 말하면 이것은 ServletContextListener입니다. 즉, 웹 애플리케이션에 대한 Servlet Context를 초기화하기 위해 트리거됩니다. 다음으로, 설정이 포함된 특수 xml 구성이 어디에 있는지 Spring에 알려주는 설정을 지정해야 합니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
보시다시피 이것은 서블릿 컨텍스트 수준에 저장되는 일반적인 설정이지만 애플리케이션 컨텍스트를 초기화할 때 Spring에서 사용됩니다. 이제 모든 서블릿 대신 다른 모든 요청을 배포하는 단일 디스패처를 선언해야 합니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
그리고 여기에는 마법이 없습니다. 살펴보면 이는 Spring이 프레임워크를 만드는 많은 작업을 수행하는 HttpServlet입니다. 남은 것은 특정 URL 템플릿을 서블릿과 연관(매핑)하는 것입니다.
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
모든 것이 이전과 동일합니다. 이제 웹 서버가 표시해야 하는 것을 만들어 보겠습니다. 예를 들어, WEB-INF에 페이지 하위 디렉토리를 생성하면 hello.jsp 파일이 있을 것입니다. 내용은 가장 원시적일 수 있습니다. 예를 들어, html 태그 안에는 " Hello World " 라는 텍스트가 포함된 h1 태그가 있습니다 . applicationContext.xml그리고 앞서 지정한 파일을 생성하는 것을 잊지 마세요 . Spring 문서의 예를 들어보자: " 1.10.3. 자동으로 클래스 감지 및 빈 정의 등록 ".
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
왜냐하면 이런 방식으로 자동 감지를 활성화하면 이제 2개의 클래스를 생성할 수 있습니다(특별한 Spring 주석 사용으로 인해 Spring Bean으로 간주됨). 이제 Spring은 이 클래스를 자체적으로 생성하고 도움을 받아 애플리케이션을 사용자 정의합니다.
  1. Java 스타일 구성의 예를 들어 웹 구성:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    이 예제는 Spring Framework 문서 " 1.11. MVC Config "에 설명되어 있습니다.

    여기에서는 JSP 페이지의 위치를 ​​결정하는 데 도움이 되는 ViewResolver를 등록합니다. 두 번째 방법은 " 기본 서블릿 "이 활성화되어 있는지 확인합니다.

    이에 대한 자세한 내용은 " default-servlet-handler의 필요성과 사용은 무엇입니까 "에서 읽을 수 있습니다.

  2. 특정 JSP에 대한 요청 매핑을 설명하기 위한 HelloController 컨트롤러

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    여기서는 " 1.4. 주석이 달린 컨트롤러 " 장의 문서에 설명된 @Controller 주석을 사용했습니다 .

이제 애플리케이션이 배포될 때 요청을 보내면 /webproject/hello(여기서 /webproject는 컨텍스트 루트임) DispatcherServlet이 먼저 처리됩니다. 그는 주 디스패처로서 우리가 /* 현재 요청과 일치하는지 결정할 것입니다. 이는 DispatcherServlet이 무언가를 해야 함을 의미합니다. 그런 다음 찾은 모든 매핑을 살펴봅니다. /hello에 매핑된 핸들 메서드가 있는 HelloController가 있음을 확인하고 이를 실행합니다. 이 메서드는 "hello"라는 텍스트를 반환합니다. 이 텍스트는 ViewResolver에 의해 수신되며 클라이언트에 표시되어야 하는 jsp 파일을 찾을 위치를 서버에 알려줍니다. 따라서 클라이언트는 궁극적으로 매우 소중한 페이지를 받게 됩니다.

결론

"컨텍스트"라는 단어가 무섭지 않다는 것이 기사를 통해 분명해졌으면 좋겠습니다. 그 사양은 매우 유용한 것으로 판명되었습니다. 그리고 문서화는 우리의 적이 아니라 친구입니다. Spring이 무엇을 기반으로 하는지, 어떻게 연결하는지, Servlet API가 Spring과 어떤 관련이 있는지가 명확해졌으면 좋겠습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION