organize/스프링

스프링 웹 프로젝트 10

001cloudid 2025. 1. 8. 22:56
728x90

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());
	}
}

728x90

'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