Spring
Basic
웹 개발 기초

웹 개발의 기초

서블릿(Servlet)

클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술로써 간단히 말하면 자바를 사용해서 웹을 만들기 위해 필요한 기술이고 조금 더 세부적으로 말하면 클라이언트가 어떠한 요청을 하면 그에 대한 request를 받고 response를 전송해줘야 하는데 이러한 역할을 하는 자바 프로그램입니다.

서블릿이 있기 이전에는

철처 - 김영한 스프링 MVC

다음과 같은 과정들을 모두 개발자가 처리해줬습니다. 이러한 과정을 간략화해서 비즈니스 로직 실행과 같은 중요한 로직에만 집중할 수 있게 해주는 것이 서블릿입니다.

서블릿의 특성

  • 서블릿은 싱글톤으로 관리된다.
    • 공유 변수 사용주의
  • 톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너라고 한다.

서블릿 컨테이너(Servlet Container)란?

서블릿의 생명주기를 관리하고 HTTP 요청이 들어올 때마다 Thread를 하나 생성하고 HTTP 요청을 처리하는 하며 서블릿과 톰캣같은 웹 서버와 소켓통신을 담당한다.

간단히 말해서

@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class TestCode extends HttpServlet {
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response) {
    System.out.println("Hello, world!");
  }
}

서블릿은 다음과 같이 사용하는데 이때 서블릿 컨테이너가 @WebServlet 어노테이션을 보고 서블릿을 생성하고 관리하게 된다.

서블릿 프로젝트

서블릿을 사용할 때는 주로 JSP를 사용해야하기 때문에 WAR를 선택해서 사용해주게 된다.

HttpServletRequest

HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱해서 HttpServletRequest 객체에 담아서 제공한다.

임시 저장

해당 HTTP 요청이 시작해서 끝날 때까지 유지되는 임시 저장소 기능을 가지고 있다.

  • 저장 : request.setAttribute(name, value)
  • 조회 : request.getAttribute(name)

세션 관리

request.getSession(create: true)

Spring

Spring Boot

단순히 실행되며, 프로덕션 제품 수준의 스프링 기반 어플리케이션을 쉽게 만들 수 있다.
XML 구성 요구사항이 전혀 없다.

  1. 어플리케이션 개발에 필수 요소들만 모아두었다.
  2. 간단한 설정으로 개발 및 커스텀이 가능하다.
  3. 간단하고, 빠르게 어플리케이션 실행 및 배포가 가능하다.
  4. 대규모프로젝트(운영환경)에 필요한 비 기능적

Initializer 없이 Project 설정

Maven 중앙 Repository 지정

최상단 부모 프로젝트에 Maven 중앙 Repository를 사용하도록 지정한다.

parents project build.gradle
allprojects{
    repositories {
        mavenCentral()
    }
}

Spring 의존성 주입

최상단 부모 프로젝트의 gradle 파일에 필요한 종속성을 넣고 자식 모듈에서도 해당 종속성을 주입한다.

부모 프로젝트
plugins {
  // Spring 설정
  id 'org.springframework.boot' version '2.7.14'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
자식 모듈
plugins {
  /// Spring 설정 version은 부모의 버전을 따라간다.
  id 'org.springframework.boot'
	id 'io.spring.dependency-management'
  ... 생략
}

dependencies {
  // Spring
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
  ... 생략
}

Spring Main 설정

Spring Main class의 이름은 보통 ~~~Application 으로 설정한다.

ApiApplication.java
// exclude = {DataSourceAutoConfiguration.class } : 
// 자동으로 DB 연결할려고 하는거 방지
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class ApiApplication {
  public static void main(String[] args) {
    SpringApplication.run(ApiApplication.class, args);
  }
}

boot.jar

Spring 프로젝트를 build하면 boot.jar 파일이 생성되는데 이 파일을 콘솔에서

java -jar boot.jar

를 통해서 실행할 수 있게 해준다.

  • *-plain.jar : Java class가 패키징된 파일
  • *.jar : Spring으로 패키징된 파일

따라서 build.gradle에서 jar는 false bootJar는 true로 설정해주고

build.gradle(:api)
bootJar {
    enabled = true
}
 
jar {
    enabled = false
}
./gradlew clean
./gradlew build

명령어를 실행하게 되면 api/build/libs/ 경로에 원하는 jar 파일만 생성되게 된다.

다른 패키지 Bean 등록

api와 db 모듈을 나눴다고 가정할 때, org.example.api, org.example.db와 같이 패키지 경로가 다르다. Bean에 등록되는 클래스는 같은 패키지 경로에 있는 클래스에 한정해서 가능하다. 그렇기 때문에 api 모듈에서 db 모듈에 있는 class에 Bean을 통해서 접근하기 위해서는 Configuration 파일을 생성하거나 같은 경로로 경로를 맞줘야 한다.

같은 경로로 맞추는 방법은 만약에 org.example.api, org.example.db가 있다면 db의 폴더명을 api로 변경하면 된다.

다른 패키지 경로를 사용하고 싶은데 Bean을 등록하는 방법은 아래와 같이 Configuration 파일을 생성하면 된다.

org.example.delivery.api.config.jpa.JpaConfig.java
@Configuration
@EntityScan(basePackages = {"org.delivery.db"})
@EnableJpaRepositories(basePackages = {"org.delivery.db"})
public class JpaConfig {
}
ℹ️
  • EntityScan : Entity 클래스가 있는 패키지를 지정한다. 지정된 패키지에서 Entity 클래스를 찾아서 등록한다.
  • EnableJpaRepositories : 해당하는 경로에서 JpaRepository extends한 인터페이스를 찾아서 AutoWire 해준다.

위와 같이 Config File을 생성해주고 나면 api 모듈에서 db 모듈에 잇는 repository나 entity 등에 접근할 수 있게 된다.

Object Mapper Custom

Spring Boot에서는 기본적으로 Object Mapper를 사용하는데 이는 content-type이 Json으로 들어오는 데이터에 대해서 직렬화/역직렬화를 하는 역할을 한다.
그리고 Spring에서는 ISO8061 방식의 데이터 포맷을 사용하는데 이를 모든 Model에다가 적용하는 것보다 처음부터 Bean에 Configuration으로 등록을 해준다면 좀 더 코드를 깔끔하게 관리할 수 있다.

/config/objectmapper/ObjectMapperConfig
@Configuration
public class ObjectMapperConfig {
  @Bean
  public ObjectMapper objectMapper() {
    var objectMapper = new ObjectMapper();
    // JDK 버전 이후 클래스 사용 가능
    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule());
 
    // 모르는 Json Value에 대해서는 무시하고 나머지 값만 파싱한다.
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    // 비어있는 Bean을 만들 때
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    // 날짜 관련 직렬화 설정 해제
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    // 스네이크 케이스
    objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
 
    return objectMapper;
  }
}

Swagger

Swagger는 이미 사용해봐서 알고 있지만 API 요청에 대한 UI를 제공하는 Open Source이다.
Spring에서 주로 사용하게 되는 것은 SpringDoc OpenAPI UI (opens in a new tab)이다.