ex02에서 Tomcat으로 서버를 구동했을 때 아래와 같은 오류가 발생함.
- 아 래 -
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hikariConfig' defined in ServletContext resource [/WEB-INF/spring/root-context.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are: PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'driverClassName' threw exception; nested exception is java.lang.NoClassDefFoundError: Unable to find Log4j2 as default logging library. Please provide a logging library and configure a valid spyLogDelegator name in the properties file.
- 끝 -
ex01은 hikariCP를 사용하지 않아서 제대로 서버가 구동되는데, ex00도 마찬가지로 위의 오류 메시지가 발생하면서 서버가 제대로 구동되지 못함.
이 오류는 Log4j2 로깅 라이브러리가 누락되어 발생함.
Log4j2는 Spring과 HikariCP가 로그를 기록하는 데 사용하는 로깅 라이브러리 중 하나인데, Spring이나 HikariCP와 같은 라이브러리는 로깅을 처리하는 데 외부 로깅 라이브러리(Log4j, Logback 등)를 필요로 함. HikariConfig의 초기화 과정에서 로그를 기록하려고 할 때, 해당 로그를 처리할 로깅 라이브러리가 없으면 NoClassDefFoundError가 발생.
이를 해결하기 위해 pom.xml에 두 가지 라이브러리를 추가해줌으로서 해결함
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0.1</version>
</dependency>
왜 이것으로 해결되는 것인지에 대한 궁금증이 생김
- log4j-api
Log4j2의 API를 제공하는 라이브러리, 이 라이브러리를 통해 애플리케이션에서 로그를 작성하고, 로그 수준, 로그 출력 등을 설정할 수 있음 - log4j-core
실제 로깅 기능을 수행하는 구현체, 이 라이브러리가 로깅 메시지를 콘솔, 파일 등으로 출력할 수 있게 해줌
log4j-api와 log4j-core 의존성을 추가하면, Log4j가 애플리케이션에 포함되어 HikariCP나 Spring에서 로그를 기록할 수 있게 됨. 이로 인해 NoClassDefFoundError가 발생하지 않게 되고, HikariConfig 빈을 정상적으로 생성할 수 있게 됨
로깅 라이브러리가 없으면 Spring이나 HikariCP가 내부에서 로그를 기록할 때 오류가 발생할 수 있음. log4j-api와 log4j-core 의존성을 추가하여 이 문제를 해결
프레젠테이션(웹) 계층의 CRUD 구현
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.zerock.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {
private final BoardService boardService;
@GetMapping("/list")
public void list(Model model) { // Model 전달 당시에는 없는데 화면에 추가적인 데이터가 필요한 경우 사용
log.info("BoardController list()................");
model.addAttribute("list", boardService.getList());
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>board/list</title>
</head>
<body>
<h1>list page</h1>
${list}
</body>
</html>
BoardController의 등록처리
- POST 방식으로 처리되는 데이터를 BoardVO 타입의 인스턴스로 바인딩해서 메서드에서 활용
- BoardService를 이용해서 등록 처리
- 'redirect:'를 이용해서 다시 목록으로 이동
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {
private final BoardService boardService;
(...생략...)
@PostMapping("/register")
public String register(BoardVO boardVO, RedirectAttributes redirectAttributes) {
log.info("BoardController register()....................");
log.info("register : " + boardVO);
redirectAttributes.addFlashAttribute("result : " + boardVO.getBno());
return "redirect:/board/list";
}
}
package org.zerock.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import lombok.AllArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({
"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@Log4j
public class BoardControllerTests {
@Setter(onMethod_ = {@Autowired})
private WebApplicationContext ctx;
private MockMvc mockMvc;
(...생략...)
@Test
public void testRegister() throws Exception{
log.info(
mockMvc.perform(
MockMvcRequestBuilders.post("/board/register").param("title", "Test 테스트")
.param("content", "content")
.param("writer", "user1"))
.andReturn()
.getModelAndView()
.getModelMap());
}
}
'organize > 스프링' 카테고리의 다른 글
스프링 웹 프로젝트 12 (0) | 2025.01.11 |
---|---|
스프링 웹 프로젝트 11 (0) | 2025.01.09 |
스프링 웹 프로젝트 9 (0) | 2025.01.07 |
스프링 웹 프로젝트 8 (0) | 2025.01.06 |
스프링 웹 프로젝트 7 (0) | 2025.01.05 |