데일리 CS 학습
- Q. 객체지향의 4가지 특징을 설명해주세요.
- A. 캡슐화: 데이터와 메서드를 하나로 묶고 접근 제어자로 외부에 노출할 것만 공개
상속: 부모 클래스의 속성, 기능을 자식이 물려받아 재사용성 향상
다형성: 같은 타입 참조변수로 여러 구현체를 다루는 것(오버라이딩, 인터페이스)
추상화: 불필요한 세부사항을 숨기고 핵심 개념만 드러내는 것 - Q. 인터페이스와 추상 클래스의 차이점과 각각 어떤 상황에 사용하는지 말해주세요.
- A. 추상클래스는 단일 상속만 가능하고 상태와 일반 메서드 포함 가능하다. 공통 구현을 공유할 때 사용합니다.
인터페이스는 다중 구현이 가능하고 서로 다른 계층에 같은 역할을 부여할 때 사용합니다.
핵심 원칙 : 공통 구현 공유 -> 추상 클래스, 역할 명세 -> 인터페이스 - Q. 오버로딩과 오버라이딩을 비교 설명해주세요
- A. 오버로딩은 같은 이름의 메서드를 매개변수만 다르게 여러 개 정의하는 것으로, 컴파일 시점에 결정됩니다. 오버라이딩은 부모 메서드를 자식에서 재정의하는 것으로 런타임에 결정되고 다형성의 핵심입니다.
- Q. JVM의 메모리 구조를 설명해주세요.
- A. 크게 세 가지입니다. Method Area에는 클래스 메타데이터와 static 변수가, Heap에는 new로 생성된 객체가 저장되고 GC가 관리합니다. Stack은 스레드마다 존재하며 메서드 호출 시 지역변수와 매개변수를 저장하고, 메서드 종료 시 자동으로 해제됩니다.
- Q. GC란 무엇인지 설명해주세요.
- A. Heap에서 더 이상 참조되지 않는 객체를 자동으로 메모리에서 해제하는 기능입니다.
- Q. final 키워드를 변수,메서드,클래스에 사용하면 각각 어떻게 되나요?
- A. final 변수는 초기화 후 재할당 불가, final 메서드는 오버라이딩 불가, final 클래스는 상속 불가입니다. String,Integer 같은 클래스가 final인 이유가 상속을 막아 불변성을 보장하기 위함입니다. 멀티스레드 환경에서 안전한 불변 객체를 설계할 때 주로 활용합니다.
- Q. 접근 제어자 4가지를 설명하고 캡슐화의 관계를 말해주세요.
- A. 넓은 순서로 public -> protected -> default -> private입니다. 캡슐화의 관점에서는 필드를 private로 숨기고 getter/setter만 공개합니다. 이렇게 하면 내부 구현을 바꿔도 외부에 영향이 없고 접근 시 유효성 검증도 추가할 수 있습니다.
Spring MVC 동작 원리
DispatcherServlet · HandlerMapping · ArgumentResolver · MessageConverter
요청 하나가 들어와서 응답으로 나가기까지 — 스프링이 내부에서 하는 모든 일을 단계별로 파헤친다
01 Spring MVC 전체 요청 흐름
HTTP 요청이 들어와 응답으로 나가기까지 Spring MVC 내부에서 일어나는 전체 흐름입니다. 이 흐름을 이해하는 것이 Spring 웹 개발의 핵심입니다.
02 DispatcherServlet — 프론트 컨트롤러
DispatcherServlet은 Spring MVC의 프론트 컨트롤러(Front Controller)입니다. 모든 HTTP 요청을 단일 진입점에서 받아 적절한 핸들러에게 위임하고 응답을 조율합니다.
과거에는 각 URL마다 별도의 서블릿을 만들어 관리했습니다. 프론트 컨트롤러 패턴은 단 하나의 서블릿이 모든 요청을 받고, 공통 처리(인증, 로깅, 인코딩 등)를 중앙화한 뒤 개별 컨트롤러에 위임합니다. Spring Boot는 자동으로
/* 경로로 DispatcherServlet을 등록합니다.// DispatcherServlet 핵심 로직 (개념적 표현)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// 1. HandlerMapping으로 핸들러(컨트롤러 메서드) 조회
HandlerExecutionChain handler = getHandler(request);
// 2. 핸들러를 실행할 수 있는 HandlerAdapter 조회
HandlerAdapter adapter = getHandlerAdapter(handler.getHandler());
// 3. 인터셉터 preHandle 실행
handler.applyPreHandle(request, response);
// 4. HandlerAdapter로 핸들러 실행 → ModelAndView 반환
ModelAndView mv = adapter.handle(request, response, handler.getHandler());
// 5. 인터셉터 postHandle 실행
handler.applyPostHandle(request, response, mv);
// 6. 뷰 렌더링 or @ResponseBody 응답 처리
processDispatchResult(request, response, handler, mv, null);
}JavaSpring Boot에서 DispatcherServlet 자동 등록
# application.yml — DispatcherServlet 경로 변경 (기본값: /)
spring:
mvc:
servlet:
path: /api # /api/* 경로만 DispatcherServlet이 처리YAML03 HandlerMapping — 핸들러 조회
요청 URL과 HTTP 메서드를 보고 어떤 컨트롤러의 어떤 메서드를 호출할지 결정합니다. 스프링은 여러 HandlerMapping을 우선순위 순으로 순회하며 핸들러를 찾습니다.
@RequestMapping, @GetMapping 등 어노테이션 기반 매핑. 실무에서 사실상 유일하게 사용.
스프링 빈 이름이 URL 경로와 일치할 때 매핑. 과거 XML 설정 방식에서 사용. 현재는 거의 안 씀.
// RequestMappingHandlerMapping이 처리하는 어노테이션들
@RestController
@RequestMapping("/members")
public class MemberController {
@GetMapping("/{id}") // GET /members/{id}
public MemberDto get(...) { ... }
@PostMapping // POST /members
public MemberDto create(...) { ... }
@PutMapping("/{id}") // PUT /members/{id}
public MemberDto update(...) { ... }
@DeleteMapping("/{id}") // DELETE /members/{id}
public void delete(...) { ... }
}
// HandlerMapping이 반환하는 것은 핸들러 + 인터셉터 목록
// HandlerExecutionChain = Handler(컨트롤러 메서드) + List<HandlerInterceptor>Java04 HandlerAdapter — 핸들러 실행
HandlerAdapter는 DispatcherServlet이 다양한 종류의 핸들러를 일관된 방식으로 실행할 수 있도록 중간에서 변환해주는 어댑터입니다. 어댑터 패턴의 전형적인 적용입니다.
| HandlerAdapter | 처리 대상 | 사용 여부 |
|---|---|---|
| RequestMappingHandlerAdapter | @RequestMapping 어노테이션 기반 핸들러 |
✅ 현재 표준 |
| HttpRequestHandlerAdapter | HttpRequestHandler 인터페이스 구현체 |
△ 정적 리소스 처리 등 |
| SimpleControllerHandlerAdapter | Controller 인터페이스 구현체 (구형) |
❌ 레거시 |
DispatcherServlet은 핸들러의 구체적인 타입을 모릅니다. 어노테이션 기반 컨트롤러, 인터페이스 기반 컨트롤러 등 다양한 핸들러가 존재하기 때문에 각 타입에 맞는 어댑터가 실행을 위임받아 처리합니다. DispatcherServlet 입장에서는 어댑터를 통해 항상 동일한 방식으로 핸들러를 실행할 수 있습니다.
05 ArgumentResolver — 파라미터 바인딩
HandlerMethodArgumentResolver는 컨트롤러 메서드의 파라미터를 HTTP 요청에서 추출해 바인딩해주는 역할입니다. 어노테이션 종류에 따라 서로 다른 ArgumentResolver가 동작합니다.
// 각 어노테이션마다 전담 ArgumentResolver가 존재
@GetMapping("/members")
public List<MemberDto> getMembers(
@RequestParam String name, // 쿼리 파라미터 (?name=현수)
@RequestParam("page") int page, // 쿼리 파라미터 이름 명시
@PathVariable Long id, // URL 경로 변수 (/members/1)
@RequestBody MemberCreateRequest req, // HTTP 바디 (JSON → 객체)
@RequestHeader("Authorization") String token, // HTTP 헤더
@CookieValue("SESSION") String sessionId, // 쿠키
@ModelAttribute SearchCondition cond, // 폼 데이터 → 객체
HttpServletRequest request, // 서블릿 요청 객체 직접 주입
@AuthenticationPrincipal UserDetails user // Spring Security 인증 객체
) { ... }Java커스텀 ArgumentResolver 구현
직접 ArgumentResolver를 만들어 등록하면 특정 어노테이션이 붙은 파라미터를 원하는 방식으로 처리할 수 있습니다. 예를 들어 @LoginMember 어노테이션으로 세션에서 로그인 유저를 자동 주입하는 패턴입니다.
// 1. 커스텀 어노테이션 정의
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginMember { }
// 2. ArgumentResolver 구현
@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// @LoginMember 어노테이션이 붙은 파라미터만 처리
return parameter.hasParameterAnnotation(LoginMember.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
// 세션에서 로그인 멤버 꺼내서 반환
HttpSession session = ((HttpServletRequest) webRequest.getNativeRequest()).getSession();
return session.getAttribute("loginMember");
}
}
// 3. WebMvcConfigurer에 등록
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
}
// 4. 컨트롤러에서 사용 — 세션 꺼내는 코드가 사라짐!
@GetMapping("/my-page")
public MyPageDto myPage(@LoginMember Member loginMember) {
return memberService.getMyPage(loginMember.getId());
}Java06 MessageConverter — HTTP 메시지 변환
HttpMessageConverter는 HTTP 요청 바디 ↔ Java 객체 간 변환을 담당합니다. @RequestBody로 요청 JSON을 객체로, @ResponseBody로 객체를 응답 JSON으로 변환합니다.
Content-Type에 따른 MessageConverter 선택
| MessageConverter | 처리 미디어 타입 | 비고 |
|---|---|---|
| MappingJackson2HttpMessageConverter | application/json |
기본값, Jackson 사용 |
| StringHttpMessageConverter | text/plain, */* |
String 타입 변환 |
| ByteArrayHttpMessageConverter | application/octet-stream |
byte[] 타입 변환, 파일 처리 |
| Jaxb2RootElementHttpMessageConverter | application/xml |
XML 변환, JAXB 사용 |
Content Negotiation — 어떻게 컨버터를 선택하나
// 요청 시 (@RequestBody): Content-Type 헤더로 읽기 컨버터 결정
// Content-Type: application/json → MappingJackson2 선택 → JSON → 객체
// 응답 시 (@ResponseBody): Accept 헤더 + 미디어타입으로 쓰기 컨버터 결정
// Accept: application/json → MappingJackson2 선택 → 객체 → JSON
// produces 설정으로 응답 타입 고정
@GetMapping(value = "/members/{id}", produces = "application/json")
public MemberDto getMember(@PathVariable Long id) { ... }
// consumes 설정으로 요청 타입 제한
@PostMapping(value = "/members", consumes = "application/json")
public MemberDto create(@RequestBody MemberCreateRequest req) { ... }
// Jackson 커스텀 설정
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule()) // LocalDateTime 직렬화
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // ISO-8601 포맷
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}Java07 ReturnValueHandler — 반환값 처리
HandlerMethodReturnValueHandler는 컨트롤러 메서드의 반환값을 HTTP 응답으로 변환합니다. ArgumentResolver의 반대 방향입니다.
// 반환 타입에 따라 처리 방식이 달라짐
// 1. ResponseEntity — 상태코드 + 헤더 + 바디 직접 제어
@GetMapping("/{id}")
public ResponseEntity<MemberDto> getMember(@PathVariable Long id) {
return ResponseEntity.ok() // 200 OK
.header("X-Custom", "value") // 커스텀 헤더
.body(memberService.findById(id)); // 응답 바디
}
// 2. 객체 직접 반환 — @ResponseBody에 의해 자동으로 JSON 직렬화
@GetMapping("/{id}")
public MemberDto getMember(@PathVariable Long id) {
return memberService.findById(id); // 200 OK + JSON 자동 변환
}
// 3. void — 응답 바디 없음 (204 No Content)
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
memberService.delete(id);
}
// 4. String — @Controller(뷰 이름 반환) vs @RestController(문자열 그대로 응답)
@Controller
public String home() {
return "home"; // ViewResolver가 templates/home.html을 찾아 렌더링
}Java08 Filter vs Interceptor vs AOP
요청/응답 전후에 공통 처리를 끼워넣는 방법이 세 가지 있습니다. 어느 시점에, 어느 범위에서 동작하는지가 핵심 차이입니다.
| 구분 | Filter | Interceptor | AOP |
|---|---|---|---|
| 동작 위치 | 서블릿 컨테이너 | DispatcherServlet 이후 | 스프링 빈 메서드 단위 |
| 스프링 컨텍스트 | 접근 어려움 | 접근 가능 | 접근 가능 |
| 적용 단위 | URL 패턴 | URL 패턴 + 컨트롤러 | 어노테이션 / 패키지 / 메서드 |
| 예외 처리 | 직접 처리 필요 | @ControllerAdvice로 처리 가능 | @ControllerAdvice로 처리 가능 |
| 주요 용도 | 인코딩, CORS, XSS 방어, 보안 | 인증/인가, 로깅, API 통계 | 트랜잭션, 로깅, 성능 측정 |
Interceptor 구현 예시
// 로그인 여부 확인 인터셉터
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
// 컨트롤러 실행 전 — false 반환 시 이후 처리 중단
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
HttpSession session = req.getSession(false);
if (session == null || session.getAttribute("loginMember") == null) {
res.sendRedirect("/login");
return false; // 컨트롤러 실행 안 함
}
return true;
}
// 컨트롤러 실행 후, 뷰 렌더링 전
@Override
public void postHandle(HttpServletRequest req, HttpServletResponse res,
Object handler, ModelAndView mv) { }
// 뷰 렌더링 후 — 예외 발생 여부와 무관하게 항상 실행
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
Object handler, Exception ex) {
// 리소스 정리, 요청 로그 마무리 등
}
}
// WebMvcConfigurer에 등록
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/**") // 모든 경로에 적용
.excludePathPatterns("/login", "/join", "/css/**"); // 제외 경로
}
}Java09 면접 예상 질문
Q1. Spring MVC의 요청 처리 흐름을 설명하세요.
Q2. DispatcherServlet이 프론트 컨트롤러인 이유는?
/*)을 받아 인코딩, 예외처리, HandlerMapping 등 공통 처리를 중앙화하고 개별 컨트롤러에 위임합니다. 덕분에 각 컨트롤러가 공통 관심사를 직접 처리하지 않아도 됩니다.Q3. ArgumentResolver와 MessageConverter의 차이점은?
@PathVariable, @RequestParam, @RequestBody 등 다양한 소스에서 데이터를 추출합니다. MessageConverter는 @RequestBody/@ResponseBody가 있을 때 HTTP 바디의 JSON과 Java 객체 간 변환만 담당합니다. 즉 ArgumentResolver가 MessageConverter를 호출해 사용하는 관계입니다.Q4. Filter와 Interceptor의 차이와 각각의 사용 사례는?
@ControllerAdvice의 예외 처리가 적용되지 않습니다. CORS, 인코딩 설정, XSS 방어 등 서블릿 레벨 공통 처리에 적합합니다. Interceptor는 DispatcherServlet 이후 스프링 컨텍스트 내부에서 동작해 스프링 빈에 접근 가능하고 @ControllerAdvice가 적용됩니다. 인증/인가, API 로깅, 요청 통계 등 스프링 빈이 필요한 처리에 적합합니다.Q5. @Controller와 @RestController의 차이는?
@Controller는 메서드 반환값을 뷰 이름으로 처리하고 ViewResolver가 템플릿 파일을 찾아 렌더링합니다. @RestController는 @Controller + @ResponseBody로, 반환값을 MessageConverter가 JSON으로 직렬화해 HTTP 응답 바디에 직접 씁니다. REST API 개발에서는 @RestController가 표준입니다.- DispatcherServlet — 모든 요청의 단일 진입점(프론트 컨트롤러). HandlerMapping → HandlerAdapter → 응답까지 전체 흐름 조율
- HandlerMapping — URL + HTTP 메서드로 핸들러 결정. 실무에서는 RequestMappingHandlerMapping만 사용
- HandlerAdapter — 다양한 핸들러 타입을 일관된 방식으로 실행. 어댑터 패턴 적용
- ArgumentResolver — 어노테이션 기반 파라미터 바인딩. 커스텀 구현으로 @LoginMember 같은 패턴 만들 수 있음
- MessageConverter — @RequestBody/@ResponseBody 시 JSON ↔ 객체 변환. Content-Type / Accept 헤더로 컨버터 선택
- Filter vs Interceptor — Filter는 서블릿 컨테이너 레벨(CORS, 인코딩), Interceptor는 스프링 컨텍스트 내부(인증, 로깅)
'스프링' 카테고리의 다른 글
| HTTP & HTTPS (0) | 2026.04.28 |
|---|---|
| Spring Bean 생명주기 (0) | 2026.04.27 |
| 예외 처리 전략 @ControllerAdvice · @ExceptionHandler · 공통 에러 응답 설계 (1) | 2026.04.20 |
| JPQL & QueryDSL객체지향 쿼리부터 동적 쿼리까지 (1) | 2026.04.17 |
| @Transactional 동작 원리 / AOP 프록시 · self-invocation · readOnly 최적화 (0) | 2026.04.16 |