반응형

서블릿 컨테이너란?

서블릿(Servlet)은 자바로 작성된 서버 측 프로그램으로, 주로 HTTP 요청과 응답을 처리하는 데 사용된다. 클라이언트가 보낸 요청을 받아 서버에서 필요한 로직을 수행하고, 그 결과를 다시 클라이언트에게 응답하는 역할을 한다.

이러한 서블릿을 실행하고 관리하는 환경을 제공하는 것이 바로 서블릿 컨테이너(Servlet Container)다. 서블릿 컨테이너는 서블릿의 생명 주기를 관리하며, 클라이언트의 요청을 적절한 서블릿에 전달하고, 서블릿이 생성한 응답을 다시 클라이언트에게 전달하는 중심적인 역할을 수행한다.

 

 

서블릿 컨테이너의 주요 역할

 

  • 서블릿 객체의 생성과 소멸 관리
    서블릿 컨테이너는 요청이 처음 들어올 때 서블릿 객체를 생성하고, 더 이상 필요하지 않을 경우 이를 메모리에서 해제하는 방식으로 자원을 효율적으로 관리한다.
  • HTTP 요청과 서블릿 매핑
    클라이언트로부터 들어온 요청의 URL을 분석하여 어떤 서블릿이 이 요청을 처리할지 결정하고, 해당 서블릿의 service 메서드를 호출한다.
  • 요청 및 응답 객체 생성
    요청 정보를 담은 HttpServletRequest 객체와, 응답 작성을 위한 HttpServletResponse 객체를 생성하여 서블릿에 전달한다.
  • 멀티쓰레딩 지원
    각 클라이언트 요청마다 별도의 쓰레드를 생성해 서블릿을 실행함으로써 여러 사용자의 요청을 동시에 처리할 수 있도록 한다.
  • 보안 및 세션 관리
    인증, 권한 검사, HTTPS 처리, 세션 유지 등 웹 애플리케이션에 필요한 다양한 기능을 제공한다.

 

 

대표적인 서블릿 컨테이너

서블릿 컨테이너는 별도의 서버로 운영되거나 애플리케이션에 내장될 수 있다. 대표적인 서블릿 컨테이너는 다음과 같다.

  • Tomcat
  • Jetty
  • Undertow
  • WildFly

Spring Boot는 기본적으로 Tomcat을 내장 서버로 사용하며, 실행 시 자동으로 서블릿 컨테이너가 함께 구동된다.

 

 

Spring Boot와의 관계

Spring Boot 애플리케이션도 내부적으로는 DispatcherServlet이라는 서블릿을 기반으로 동작한다. 이 DispatcherServlet은 서블릿 컨테이너에 의해 관리되며, 클라이언트의 요청을 받아 적절한 컨트롤러로 분기하고, 그 결과를 응답으로 구성해 반환한다.

결국 Spring Boot 애플리케이션도 서블릿 컨테이너 위에서 동작한다고 볼 수 있으며, Spring의 많은 기능들은 이 서블릿 기반 구조를 바탕으로 구현된다.

 

 

 

 

 

반응형
반응형

Resolver란?

Resolver는 요청이나 데이터를 해석하여 필요한 객체나 값으로 변환하거나 찾아주는 컴포넌트를 의미합니다.

쉽게 말해, 클라이언트가 보낸 요청이나 특정 데이터를 내부적으로 필요한 형태(예: 객체, 값 등)로 가공하거나 찾아주는 역할을 합니다.
"Resolve"라는 단어 자체가 "해결하다", "변환하다"는 뜻을 가지고 있기 때문에, Resolver는 어떤 것을 해석해서 알맞은 형태로 바꿔주는 기능이라고 이해하면 됩니다.

 

Spring Boot에서의 Resolver 의미와 종류

1. HandlerMethodArgumentResolver (HTTP 요청 파라미터 해석기)

Spring MVC에서 컨트롤러 메서드의 파라미터를 해석해서 값으로 주입하는 컴포넌트입니다.
예를 들어, 다음과 같은 컨트롤러 메서드를 생각해봅시다.

@GetMapping("/user")
public String getUser(@CurrentUser User user) { 
	return user.getName();
 }

여기서 @CurrentUser는 커스텀 어노테이션이고, 이 값을 주입하려면 HandlerMethodArgumentResolver를 구현해야 합니다.

예)

public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(CurrentUser.class) != null &&
               parameter.getParameterType().equals(User.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) {
        // 실제 사용자 객체를 반환
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}

이후 WebMvcConfigurer에서 등록해 줍니다:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CurrentUserArgumentResolver());
    }
}
 

2. ViewResolver (뷰 이름을 템플릿으로 매핑)

Spring MVC에서 컨트롤러가 반환한 뷰 이름을 실제 뷰(template)로 변환하는 역할입니다.

@Controller
public String hello(Model model) {
    model.addAttribute("msg", "Hello");
    return "greeting"; // ViewResolver가 greeting.html로 연결
}

 

반응형
반응형

스프링 표준 코딩 작성 방법

  1. 적절한 패키징 스타일
    • 적절한 패키징은 코드와 애플리케이션의 흐름을 쉽게 이해하는 데 도움이 됩니다.
    • 의미 있는 패키징으로 애플리케이션을 구조화할 수 있습니다.
    • 컨트롤러를 별도의 패키지에, 서비스를 별도의 패키지에, 유틸 클래스를 별도의 패키지에 포함시킬 수 있습니다... 등. 이 스타일은 소규모 마이크로서비스에서 매우 편리합니다.
    • 거대한 코드 베이스에서 작업하는 경우, 기능 기반 접근 방식을 사용할 수 있습니다. 요구사항에 따라 결정할 수 있습니다.
  2. 디자인 패턴 사용
    • 어디에 사용할지는 파악해야합니다.
  3. 스프링 부트 스타터 사용
    • 단일 종속성을 하나하나 추가하지 않고 매우 쉽게 스타터 종속성을 사용할 수 있습니다. 이러한 스타터 종속성은 이미 필요한 종속성과 함께 번들로 제공됩니다. 예를 들어, spring-boot-starter-web 종속성을 추가하면 기본적으로 jackson, spring-core, spring-mvc 및 spring-boot-starter-tomcat 종속성이 번들로 제공됩니다. 따라서 우리는 별도로 종속성을 추가할 필요가 없습니다. 또한 버전 불일치를 피하는 데 도움이 됩니다.
  4. 적절한 라이브러리 버전 사용
    • 언제나 최신 안정적인 GA(General Availability) 버전을 사용하는 것이 권장됩니다. 때때로 이는 Java 버전, 서버 버전, 애플리케이션의 유형 등에 따라 달라질 수 있습니다. 동일한 패키지의 다른 버전을 사용하지 않고 여러 종속성이 있는 경우 항상 <properties>를 사용하여 버전을 지정하십시오.
  5. Lombok 사용
    • Lombok은 자바 라이브러리로, 해당 어노테이션을 사용하여 코드를 줄이고 깔끔한 코드를 작성할 수 있도록 도와줍니다. 예를 들어, 엔티티, 요청/응답 객체, DTO 등과 같은 몇 가지 클래스에서는 getter와 setter를 위해 많은 줄을 작성해야 하는데 Lombok을 사용시 한 줄로 처리될 수 있습니다. 필요에 따라 @Data, @Getter 또는 @Setter를 사용할 수 있습니다. 또한 롬복 로거 어노테이션을 사용할 수도 있습니다. @Slf4j를 권장합니다.
  6. Lombok을 이용한 생성자 주입
    • 의존성 주입(Dependency Injection)에서 생성자 주입을 강력히 권장하는데, Lombok의 @RequiredArgsConstructor 어노테이션을 사용하여 생성자 주입을 할 수 있습니다.
    • import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor public class SampleController { private final SampleService sampleService; @GetMapping("/hello") public String sayHello() { return sampleService.getGreeting(); } }
  7. slf4j logging를 사용
    • System.out.print()를 사용하지 마세요.
    • Spring Boot의 기본 로깅 프레임워크인 logback과 함께 사용하기 위해 Slf4j를 권장합니다. 항상 slf4j {}를 사용하고, 로거 메시지에 문자열 보간법(String interpolation)을 사용하지 않도록 해야 합니다. 왜냐하면 문자열 보간법은 더 많은 메모리를 사용하기 때문입니다. Lombok의 @Slf4j 어노테이션을 사용하면 로거를 매우 쉽게 생성할 수 있습니다.
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Service;
      
      @Service
      @Slf4j
      public class SampleService {
          public void performTask() {
              log.debug("Debug message");
              log.info("Info message");
              log.warn("Warning message");
              log.error("Error message");
          }
      }
      
    • ** String interpolation: ${변수명}처럼 변수명을 사용하여 문자를 출력하는 것을 말합니다.
    • 마이크로서비스 환경에서는 ELK 스택을 사용할 수 있습니다.
  8. 컨트롤러는 라우팅으로만 사용
    • 컨트롤러는 상태를 가지지 않으며 싱글톤입니다.
    • 비즈니스 로직은 컨트롤러에 위치해서는 안 됩니다.
  9. 서비스에서 비즈니스 로직 구현
    • 서비스는 싱글톤입니다.
    • 비즈니스 로직은 여기에 포함되며, 유효성 검사, 캐싱 등이 이루어집니다.
    • 서비스는 영속성 계층과 통신하고 결과를 받아옵니다.
  10. NullPointerException 피하기
    • java.util 패키지의 Optional, Apache Commons StringUtils을 사용하여 NullPointerException을 피할 수 있습니다.
    • toString() 대신 valueOf()를 사용하세요.
    • IDE 기반의 @NotNull 및 @Nullable 어노테이션을 사용하세요.
  11. 컬렉션 프레임워크에 대한 모범 사례
    • 데이터 세트에 적합한 컬렉션을 사용하세요.
    • Java 8의 기능을 활용하여 forEach를 사용하고, 레거시 for 루프는 피하세요.
    • 구현 대신 인터페이스 유형을 사용하세요.
    • 가독성을 위해 size() 대신 isEmpty()를 사용하세요.
    • null 값을 반환하지 마세요. 대신 빈 컬렉션을 반환할 수 있습니다.
    • 해시 기반 컬렉션에 저장될 데이터로 객체를 사용하는 경우, equals()와 hashCode() 메서드를 오버라이드해야 합니다.
  12. pagination 사용
    • pagination을 사용하면 애플리케이션의 성능이 향상됩니다.
    • Spring Data JPA를 사용하는 경우, PagingAndSortingRepository를 사용하면 페이지네이션을 매우 쉽게 사용할 수 있고 노력도 적게 듭니다.
  13. 캐싱 사용
    • Spring Boot는 기본적으로 ConcurrentHashMap을 사용하여 캐싱을 제공하며, @EnableCaching 어노테이션을 통해 이를 구현할 수 있습니다.
    • 기본 캐싱에 만족하지 못하는 경우 Redis, Hazelcast 또는 기타 분산 캐싱 구현체를 사용할 수 있습니다. Redis와 Hazelcast는 메모리 내 캐싱 방법입니다. 또한 데이터베이스 캐시 구현체를 사용할 수도 있습니다.
  14. 전역 예외 처리를 위해 사용자 정의 예외 처리기를 전역 예외 처리와 같이 사용
    • 일반적인 예외 외에도 특정한 오류 상황을 식별해야 할 수 있습니다. @ControllerAdvice를 사용하여 예외 어드바이저를 만들고 의미 있는 세부 정보를 갖는 별도의 예외를 생성할 수 있습니다.
    • import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception ex) { // 예외 처리 로직 작성 // 클라이언트에게 적절한 응답을 반환하거나 로깅 등을 수행할 수 있습니다. // 예시로 500 Internal Server Error를 반환합니다. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal Server Error"); } }
  15. 사용자 정의 응답 객체 사용
    • 사용자 정의 응답 객체를 사용하면 HTTP 상태 코드, API 코드, 메시지 등과 같은 특정 데이터를 가진 객체를 반환할 수 있습니다.
    • 빌더 디자인 패턴을 사용하여 사용자 정의 속성이 있는 응답 객체를 생성할 수 있습니다.
  16. 불필요한 코드, 변수, 메서드 및 클래스 제거
    • 사용되지 않는 변수 선언은 일부 메모리를 차지합니다. 사용되지 않는 메서드, 클래스 등을 제거하세요.
    • 중첩된 반복문을 피하려고 노력하세요. 대신 맵을 사용할 수 있습니다.
  17. 주석 사용
    • 주석은 남용하지 않는 한 좋은 관행입니다. 모든 것에 주석을 달지 마세요. 대신 클래스, 함수, 메서드, 변수 등에 의미 있는 단어를 사용하여 설명적인 코드를 작성할 수 있습니다.
    • 주석을 사용하여 경고 및 처음 보는 사람에게 이해하기 어려운 내용을 설명할 수 있습니다.
  18. 클래스, 메서드, 함수, 변수 및 기타 속성에 의미 있는 단어 사용
    • 적절하고 의미 있는 이름 규칙을 사용하고 적절한 케이스를 사용하세요.
    • 일반적으로 클래스, 변수 및 상수를 선언할 때는 명사 또는 짧은 구문을 사용합니다. 예: String firstName, const isValid
    • 함수와 메서드에는 동사와 형용사와 함께 짧은 구문을 사용할 수 있습니다. 예: readFile(), sendData()
    • 변수 이름을 약어로 사용하거나 의도를 드러내는 이름을 사용하는 것을 피하세요. 예: int i; String getExUsr;
  19. 선언에 적절한 케이스 사용
    • 다양한 케이스(UPPERCASE, lowercase, camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case 등)가 있습니다. 각 변수에 어떤 케이스를 사용해야 할지를 파악해야 합니다.methods & variables — camelCaseDB-related fields — snake_case
    • constants — SCREAMING_SNAKE_CASE
    • classes — PascalCase
  20. 단순
    • 항상 간단하고 가독성이 좋은 코드를 작성하려 노력하세요. 동일한 간단한 논리를 다른 방식으로 구현할 수 있지만, 가독성이나 이해하기 어렵다면 어려울 수 있습니다. 때로는 복잡한 논리가 더 많은 메모리를 사용합니다. 코드를 작성할 때 KISS, DRY 및 SOLID 원칙을 사용하려 노력하세요. 이에 대해 나중에 기사로 설명하도록 하겠습니다.
  21. 공통적인 코드 포맷 스타일 사용
    • 코딩 스타일은 개발자마다 다양할 수 있습니다. 코딩 스타일 변경은 변경 사항으로 간주되며, 코드 병합을 어렵게 만들 수 있습니다. 이를 피하기 위해 팀은 공통된 코딩 포맷을 가질 수 있습니다.
  22. SonarLint 사용
    • SonarLint는 코드 품질과 유지 관리성을 개선하는 데 도움이 되는 강력한 코드 분석 도구입니다. 이 도구는 업계 모범 사례와 코딩 표준을 기반으로 실시간 피드백과 제안을 제공합니다. SonarLint를 개발 환경에 통합하면 버그, 취약점, 코드 품질 문제 등을 사전에 식별하고 해결할 수 있습니다.

 

 

Reference

반응형

+ Recent posts