개요

  • Java의 로그 라이브러리에는 대표적으로 slf4j, log4j2(log4j), logback이 있다
    • 여기서 slf4j는 인터페이스 역할, log4j2 / logback은 구현체 역할이라고 보면 된다
    • slf4j를 활용하면 log4j2 <-> logback 간의 마이그레이션을 간단히 할 수 있다
      • 인터페이스의 역할
  • 일반적으로 slf4j에 logback 또는 log4j2 둘 중 하나를 사용한다

 

SLF4J(Simple Logging Facade For Java)

특징

  • slf4j는 로깅에 대한 추상 레이어를 제공하는 인터페이스의 모음이다
  • Facade 디자인 패턴이 적용되어 있다
  • 구현 클래스를 사용자가 설정할 수 있다

 

SLF4J의 세 가지 모듈

1. SLF4J API

  • SLF4J를 사용하기 위한 인터페이스를 제공한다
  • slf4j-api-{version}.jar를 통해 사용한다

 

2. SLF4J Binding(2.0.0 버전부터 Provider)

  • SLF4J 2.0.0 버전부터는 Binding이라는 용어가 Provider로 변경되었다.
  • slf4j 바인딩은 하나만 사용해야 한다
  • SLF4J 인터페이스를 로깅 구현체와 연결하는 역할
  • SLF4J Binding의 종류
    • slf4j-log4j12-{version}.jar
      • log4j와 log4j2에 대한 바인딩
      • log4j (log4j 1.x)에 대한 지원 중단
        • log4j 1.x가 2015년에 EOL 되었고, 2022년 SLF4j 1.7.35부터는 log4j에 대한 것은 빌드 시 자동으로 slf4j-reload4j 모듈로 리다이렉션 됨
        • log4j 1.x를 사용하고 싶다면 slf4j-reload4j를 사용하는 것이 좋음
    • slf4j-reload4j-{version}.jar
      • log4j 1.2.7의 대체
    • slf4j-jdk14-{version}.jar
      • JDK 1.4 로깅(java.util.logging)에 대한 바인딩/공급자
    • logback-classic-{version}.jar + logback-core-{version}.jar
      • Logback의 클래스는 SLF4J의 인터페이스를 직접 구현한 것
      • version = 1.4.x : 구현체로 logback을 사용하고, Jakarta EE를 사용할 때의 바인딩
      • version = 1.3.x : 구현체로 logback을 사용하고, Javax EE를 사용할 때의 바인딩

 

3. SLF4J Bridging Modules

  • 다른 로깅 API로 로거를 호출할 때 SLF4J 인터페이스로 연결하여 대신 처리할 수 있도록 하는 역할

 

사용 예제(maven)

pom.xml

<dependencies>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.6</version>
    </dependency>

</dependencies>
  • slf4j에 대한 dependency를 추가
  • pom.xml을 변경한 후에는 반드시 변경사항을 반영해주어야 한다
    • 인텔리제이 기준 우측 상단의 “Load Maven Changes”을 클릭하거나 <Ctrl + Shift + O>를 입력
package org.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

    private final static Logger logger = LoggerFactory.getLogger(Main.class);

    public void doSomething() {
        logger.info("Logging Something");
    }

    public static void main(String[] args) {
        new Main().doSomething();
    }
}
  • slf4를 사용하여 로그 출력 시도
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See <https://www.slf4j.org/codes.html#noProviders> for further details.
  • 이와 같은 오류 메시지가 출력되며 로깅이 되지 않는다
    • SLF4J 2.0.0 버전 이상부터는 binding이라는 용어가 provider로 대체되었다
  • 그 이유는 SLF4J는 인터페이스 역할이기 때문에 구현체가 필요하기 때문이다
  • 이를 위해서는 SLF4J Binding jar 파일을 정확히 1개 두어야 한다
    • 여기서는 logback에 대한 바인딩인 logback-classic을 사용하여 진행

pom.xml에 logback dependency 추가

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.5</version>
</dependency>
  • 예제에서는 1.4.5 버전을 사용

출력 결과

17:24:49.264 [main] INFO org.example.Main - Logging Something

 

참고 자료

상황

  • 프로젝트를 시작할 당시에는 gradle을 사용하지 않았음
    • 프로젝트를 진행함에 따라 gradle을 적용하려고 함
  • 하나의 루트 프로젝트(CacheServerProject)와 두 개의 프로젝트(Server, Client)로 이루어짐
    • Server와 Client
    • 일반적인 gradle의 디렉토리 구조와 상이함
      • src/main/java와 같은 구조로 설계하지 않음
  • 이 두 개의 프로젝트를 유지하며 gradle을 사용하고자 함
    • Server와 Client 각각 jar 파일을 생성할 수 있어야 함

적용

  • gradle을 적용하기 위해서는 두 개의 gradle 관련 파일을 만들어야 함

    • build.gradle
    • settings.gradle
  • build.gradle

      plugins {
          id 'java'
      }
    
      group 'org.example'
      version '1.0-SNAPSHOT'
    
      repositories {
          mavenCentral()
      }
    
      dependencies {
          testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
          testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
      }
    
      test {
          useJUnitPlatform()
      }
  • InteliJ에서 gradle로 프로젝트 생성시 초기에 주어지는 설정을 그대로 복사함

  • settings.gradle

      rootProject.name = 'CacheServerProject'
      include 'Server'
      include 'Client'
    • 처음에는 rootProject.name만 설정하였음
    • 여기에서 멀티 프로젝트의 이름을 기입하면 됨
      • include ‘[프로젝트 이름]’
  • 성공적으로 gradle을 적용한 후에 디렉토리 구조는 다음과 같음

    • CacheServerProject ⇒ Root Project
      • Client
        • src/main/java/com/blue/cacheserver
          • message
          • start
          • test
          • 이하 디렉토리 생략
        • build.gradle
      • Server
        • src/main/java/com/blue/cacheserver
          • cache
          • client
          • message
          • service
          • task
          • 이하 디렉토리 생략
        • build.gradle
      • gradle
      • .gitignore
      • build.gradle
      • gradlew
      • gradlew.bat
      • README.md
      • settings.gradle
  • gradle을 이용해 build 하게 되면 Client와 Server 디렉토리에 build 디렉토리가 생성됨

    • 하위의 libs로 가면 jar 파일이 생성되어 있음

    • 하지만 실행해보면 main-class를 찾지 못했다면서 실행되지 않음

    • 다음 내용을 Client, Server 프로젝트의 build.gradle에 추가

      • ex) Main Class의 경로가 com.blue.cacheserver.start.ClientMain 일 때

        jar {
          manifest {
              attributes 'Main-Class': 'com.blue.cacheserver.start.ClientMain'
          }
        }
      • 이 설정을 추가하면 정상적으로 main 클래스를 인식함

'Study > Java' 카테고리의 다른 글

[Logger] SLF4J 기초  (0) 2022.12.22
[JSP] JSP에서의 session 관련 메소드 학습  (0) 2022.11.28
[디자인 패턴] 싱글톤(Singleton) 패턴  (0) 2022.11.16
[InteliJ] InteliJ Settings  (0) 2022.11.12
[Java] 코딩테스트 관련 문법  (0) 2022.11.10

Session 개요

Session

  • Web Server에서 상태를 유지하기 위한 정보
    • http의 Stateless를 보완
    • 브라우저를 닫거나, 세션 만료 시간이 지나거나, 서버에서 세션을 삭제했을 때 제거
  • 세션은 사용자의 정보를 유지하기 위해 javax.servlet.http 패키지의 HttpSession 인터페이스를 구현해서 사용
  • 쿠키보다 좀 더 안정적이고, 보안성이 높다고 할 수 있음
    • 쿠키는 사용자의 컴퓨터에 저장되고 세션은 서버에 저장됨
  • 세션은 각 브라우저 당 1개씩 생성되어 웹 컨테이너에 저장

주요 Session Method

  • void setAttribute(String name, Object value)
    • 속성 명을 name으로, 값을 value로 할당
    • 이미 존재하는 속성 명이라면 덮어씌움
  • Object getAttribute(String name)
    • 세션 속성 명이 name인 속성의 값을 Object 타입으로 리턴
    • 해당 name이 없을 경우 null을 리턴
  • void removeAttribute(String name)
    • 속성 명이 name인 속성을 삭제
  • Enumeration getAttributesNames()
    • 모든 세션 속성 이름을 Enumeration 타입으로 리턴
  • long getCreationTime()
    • 기준 시간부터 현재 세션이 생성된 시간까지 경과한 시간을 계산
    • 기준 시간은 1970-01-01-00-00
  • String getId()
    • 세션에 할당된 고유 식별자를 String 타입으로 리턴
    • 즉, 세션의 ID를 리턴함
  • int getMaxInactiveInterval()
    • 현재 세션에 설정된 유지 시간을 리턴
  • void setMaxInactiveInterval(int interval)
    • 세션 유지 시간을 설정(단위: Seconds)
  • void invaildate()
    • 현재 세션을 invalidate함

Session Attribute

세션 객체는 웹 브라우저와 매핑됨

  • 해당 웹 브라우저를 닫을 때까지 같은 창에서 열린 페이지는 같은 Session 객체를 공유
  • 세션의 속성 값은 객체 형태만 가능함

Eclipse를 통한 JSP 생성 및 war로 배포

Step 1. 환경설정

  • tomcat, eclipse, jdk가 준비되어 있어야 함
  • Tomcat : Tomcat v9.0 Server
  • Eclipse : 2022-09 / Eclipse IDE for Enterprise Java and Web Developers 패키지
  • JDK : 1.8

Step 2. 프로젝트 생성

  • 메뉴의 [File] → [New] → [Other]에서 Server → Server를 선택 → Next
  • 해당 글에서는 server type을 Apache → Tomcat v9.0 Server로 진행 → Next
  • Tomcat 설치 디렉토리를 설정해주거나, Download and Install로 톰캣을 다운로드
    • 해당 글에서는 Download and Install 이용
    • /home/user/eclipse/apache-tomcat-9.0.62 디렉토리에 설치
  • JDK(JRE) 설정
    • Installed JREs… → Add → Standard VM → JRE home에 JDK의 home을 Directory로 설정
  • Finish를 누르면 [Servers]가 생성된 것을 확인 가능

Step 3. Dynamic Web Project 생성

  • [File] → [New] → [Dynamic Web Project]
  • Project Name을 설정한 뒤 Finish → Project Name에 대한 루트 디렉토리가 생성됨
  • [프로젝트 루트 디렉토리] → src → main → webapp(META-INF, WEB-INF가 있는 디렉토리)에서 [New] → [JSP File]
  • 파일 이름을 정하고 finish
    • 여기서는 index.jsp로 진행

Step 4. JSP 파일 내용 작성

  • 기본 동작을 확인하기 위해 간단한 테스트 코드 작성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sessionTestApp</title>
</head>
<body>
<p>JSP TEST</p>

<%
	out.println("Hello JSP");
%>

</body>
</html>
  • [상단 바] - [Run Index.jsp] → 톰캣 지정 → Next → Configured에 [Project Name]이 있는 것을 확인 후 Finish

Step 5. process.jsp 작성

  • 테스트 애플리케이션은 하이퍼링크를 통해서 특정 request를 발생시켜 테스트를 진행할 수 있음
  • 별도의 파일인 process.jsp에 들어오는 쿼리에 대한 분기를 통해 테스트 애플리케이션 작성 가능
  • 예시 파일
    • index.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>sessionTestApp</title>
    </head>
    <body>
    <h2>JSP TEST</h2>
    
    <%
    	out.println("Hello JSP");
    	String id = session.getId();
    %>
    
    </br>
    
    <%
    	out.println("id = " + id);
    %>
    
    </br>
    
    <!-- 
    ~/process.jsp?value=[텍스트 입력 값]으로 request 전송
    즉, 쿼리의 값을 사용자로부터 입력받을 수 있음
    -->
    <form action="process.jsp">
    	텍스트 입력
      <input type="text" name="value">
    </form>
    
    <tr>
    	<td align="left">Invalidate Test</td>
    	</br>
    	<td align="left"><a href="process.jsp?value=invalidate">Invalidate 실행</a></td>
    	</br>
    	<td align="left"><a href="process.jsp?value=setAttr">setAttribute 실행</a></td>
    	</br>
    	<td align="left"><a href="process.jsp?value=123">실행</a></td>
    	</br>
    </tr>
    
    <%
    	out.println("Attribute value : " + session.getAttribute("attributeName"));
    %>
    
    </body>
    </html>
    
    • process.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <%
    
    	// index.jsp에서 process.jsp로 요청할 때 [process.jsp?value=invalidate"]와 같이 요청
    	// 이 중 ? 뒤에 쿼리에서 있는 value=[값] 부분에 따라 다른 명령을 실행할 수 있음
    	if (request.getParameter("value").equals("invalidate")) {
    		session.invalidate();
    		response.sendRedirect("index.jsp");
    	} else if (request.getParameter("value").equals("setAttr")) {
    		session.setAttribute("attributeName", "testValue");
    		response.sendRedirect("index.jsp");
    	} else {
    		out.println(request.getParameter("value"));
    	}
    
    	// 위의 명령 실행 후 index.jsp 페이지로 돌아감
    	
    %>
    
    </body>
    </html>
    

Step 6. war 파일로 export

  • [File] → [Export] → WAR 검색 → [WAR file] 선택 →Next → Web project 선택(방금 만든 프로젝트 명) → 파일 경로 선택(Destination) → Optimize for a specific server runtime 체크 해제 → finish
  • Destination으로 선택한 디렉토리에 war 파일이 저장 됨

참고자료

싱글톤 패턴의 다양한 예제

기본 패턴

public class ExampleCls {
	private static ExampleCls instance;

	private ExampleCls() {}
	
	public static ExampleCls getInstance() {
		if (instance == null) {
			instance = new ExampleCls();
		}
	
		return instance;
	}
}

Synchronized 사용(Thread-Safe)

public class ExampleCls {
	private static ExampleCls instance;

	private ExampleCls() {}
	
	public static synchronized ExampleCls getInstance() {
		if (instance == null) {
			instance = new ExampleCls();
		}
	
		return instance;
	}
}
  • 동시성 문제는 해결되지만, 성능에 저하가 발생할 수 있음

(Lazy) Double check 싱글톤

public class ExampleCls {
	private volatile static ExampleCls instance;

	private ExampleCls() {}
	
	public static ExampleCls getInstance() {
		if (instance == null) {
			synchronized(ExampleCls.class) {
				if (instance == null) {
					instance = new ExampleCls();
				}
			}
		}
	
		return instance;
	}
}
  • instance가 null인 것을 확인한 후에 synchronized를 적용하여 성능 개선
  • Lazy 기법에 해당함

LazyHolder(현재 Java 싱글톤 패턴에서 가장 일반적으로 사용)

public class ExampleCls {
	private ExampleCls() {}
	
	public static class ExampleCls LazyHolder() {
		private static final ExampleCls instance = new ExampleCls();
	}

	public static ExampleCls getInstance() {
		return LazyHolder.instance;
	}
}
  • JVM의 특성을 활용한 Thread-Safe 싱글톤 패턴
  • Class를 로드 및 초기화하는 시점에 Thread-Safe를 보장하는 것을 이용한 패턴

싱글톤 패턴의 문제점

  • 객체 지향 설계에 대한 안티 패턴이 될 수 있음
    • 싱글톤에서는 생성자가 private이기 때문에 상속이 어려움

자주 쓰는 단축키

  • <Control> + <Space Bar> : 추천 코드
  • <Alt> + <Enter> : 제안 보기
  • <Alt> + <Insert> : Generate
  • <Control> + <Alt> + ←/→ : 이전/다음 커서 위치
  • <Control> + <Alt> + <L> : 자동 Indent
  • <Control> + <Alt> + <O> : 자동 Import
  • <Shift> + <Enter> : 다음 줄로 이동
  • <Control> + <Shift> + <Enter> : 자동 완성하고 다음 줄로 이동
  • <Control> + <코드 클릭> : 해당 코드의 원본 위치로 이동
  • <Shift> + <6> : Rename

세팅

  • Editor → General → Code Completion
    • Show suggestions as you type
      • 코드 타이핑 시 제안 보여주기
      • 체크 해제
    • Automatically Insert single suggestions for:
      • 한 개의 제안 이 있을 때 자동 완성
      • 체크
  • Editor → General → Code Folding
    • 코드 한 줄로 표시(Folding)
    • 체크 해제
  • General → Mouse Control → Change font size with Ctrl+Mouse Wheel in:
    • 체크 시 마우스 휠로 글자 크기 조정 가능
    • All editors에 체크 하면 모든 에디터의 글자 크기로 적용됨

+ Recent posts