63. spring(6)

sumin·2023년 10월 10일
0

아카데미

목록 보기
64/82
post-thumbnail

JDBC

JDBC Connection

package com.gdu.app09.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.springframework.stereotype.Component;

@Component  // JdbcConnection 객체(Bean)를 Spring Container에 저장해 둔다.
public class JdbcConnection {

  /*
   * 일반 버전
   *  Class.forName("oracle.jdbc.OracleDriver");
   *  url = "jdbc:oracle:thin:@localhost:1521:xe"
   * 
   * 쿼리 출력 버전(log4jdbc 디펜던시)
   *  Class.forName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
   *  url = "jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"
   */
  
  public Connection getConnection() {
    Connection con = null;
    try {
      Class.forName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
      con = DriverManager.getConnection("jdbc:log4jdbc:oracle:thin:@localhost:1521:xe", "GD", "1111");
    } catch (Exception e) {
      e.printStackTrace();
    }
    return con;
  }
  
  public void close(Connection con, PreparedStatement ps, ResultSet rs) {
    try {
      if(rs != null) rs.close();
      if(ps != null) ps.close();
      if(con != null) con.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }  
}

Dao

package com.gdu.app09.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.gdu.app09.dto.ContactDto;

@Repository
public class ContactDao {

  @Autowired  // Spring Container에 저장된 JdbcConnection 타입의 객체(Bean)을 가져온다.
  private JdbcConnection jdbcConnection;
  
  private Connection con;
  private PreparedStatement ps;
  private ResultSet rs;
  
  /**
   * 삽입 메소드<br>
   * @param contactDto 삽입할 연락처 정보(name, tel, email, address)
   * @return insertCount 삽입된 행(Row)의 개수, 1이면 삽입 성공, 0이면 삽입 실패
   */
  public int insert(ContactDto contactDto) {
    
    int insertCount = 0;
    
    try {
      
      con = jdbcConnection.getConnection();
      String sql = "INSERT INTO CONTACT_T(CONTACT_NO, NAME, TEL, EMAIL, ADDRESS, CREATED_AT) VALUES(CONTACT_SEQ.NEXTVAL, ?, ?, ?, ?, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'))";
      ps = con.prepareStatement(sql);
      ps.setString(1, contactDto.getName());
      ps.setString(2, contactDto.getTel());
      ps.setString(3, contactDto.getEmail());
      ps.setString(4, contactDto.getAddress());
      insertCount = ps.executeUpdate();
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      jdbcConnection.close(con, ps, rs);
    }    
    return insertCount;    
  }
  
  /**
   * 수정 메소드<br>
   * @param contactDto 수정할 연락처 정보(contact_no, name, tel, email, address)
   * @return updateCount 수정된 행(Row)의 개수, 1이면 수정 성공, 0이면 수정 실패
   */
  public int update(ContactDto contactDto) {
        
    int updateCount = 0;
    
    try {
      
      con = jdbcConnection.getConnection();
      String sql = "UPDATE CONTACT_T SET NAME = ?, TEL = ?, EMAIL = ?, ADDRESS = ? WHERE CONTACT_NO = ?";    
      ps = con.prepareStatement(sql);
      ps.setString(1, contactDto.getName());
      ps.setString(2, contactDto.getTel());
      ps.setString(3, contactDto.getEmail());
      ps.setString(4, contactDto.getAddress());
      ps.setInt(5, contactDto.getContact_no());
      updateCount = ps.executeUpdate();
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      jdbcConnection.close(con, ps, rs);
    }
    
    return updateCount;
    
  }
  
  /**
   * 삭제 메소드<br>
   * @param contact_no 삭제할 연락처 번호
   * @return deleteCount 삭제된 행(Row)의 개수, 1이면 삭제 성공, 0이면 삭제 실패
   */
  public int delete(int contact_no) {
    
    int deleteCount = 0;
    
    try {
      
      con = jdbcConnection.getConnection();
      String sql = "DELETE FROM CONTACT_T WHERE CONTACT_NO = ?";
      ps = con.prepareStatement(sql);
      ps.setInt(1, contact_no);
      deleteCount = ps.executeUpdate();
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      jdbcConnection.close(con, ps, rs);
    }
    
    return deleteCount;
    
  }
  
  /**
   * 전체 조회 메소드<br>
   * @return 조회된 모든 연락처 정보(ContactDto)
   */
  public List<ContactDto> selectList() {
    
    List<ContactDto> list = new ArrayList<ContactDto>();
    
    try {
      
      con = jdbcConnection.getConnection();
      String sql = "SELECT CONTACT_NO, NAME, TEL, EMAIL, ADDRESS, CREATED_AT FROM CONTACT_T ORDER BY CONTACT_NO ASC";
      ps = con.prepareStatement(sql);
      rs = ps.executeQuery();
      while(rs.next()) {
        ContactDto contactDto = new ContactDto();
        contactDto.setContact_no(rs.getInt("CONTACT_NO"));
        contactDto.setName(rs.getString("NAME"));
        contactDto.setTel(rs.getString("TEL"));
        contactDto.setEmail(rs.getString("EMAIL"));
        contactDto.setAddress(rs.getString("ADDRESS"));
        contactDto.setCreated_at(rs.getString("CREATED_AT"));
        list.add(contactDto);
      }
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      jdbcConnection.close(con, ps, rs);
    }
    
    return list;
    
  }
  
  /**
   * 상세 조회 메소드<br>
   * @param contact_no 조회할 연락처 번호
   * @return contactDto 조회된 연락처 정보, 조회된 연락처가 없으면 null 반환
   */
  public ContactDto selectContactByNo(int contact_no) {
    
    ContactDto contactDto = null;
    
    try {      
      con = jdbcConnection.getConnection();
      String sql = "SELECT CONTACT_NO, NAME, TEL, EMAIL, ADDRESS, CREATED_AT FROM CONTACT_T WHERE CONTACT_NO = ?";
      ps = con.prepareStatement(sql);
      ps.setInt(1, contact_no);
      rs = ps.executeQuery();
      if(rs.next()) {
        contactDto = new ContactDto();
        contactDto.setContact_no(rs.getInt(1));
        contactDto.setName(rs.getString(2));
        contactDto.setTel(rs.getString(3));
        contactDto.setEmail(rs.getString(4));
        contactDto.setAddress(rs.getString(5));
        contactDto.setCreated_at(rs.getString(6));
      }      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      jdbcConnection.close(con, ps, rs);
    }    
    return contactDto;
  }  
}

Service

package com.gdu.app09.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.gdu.app09.dao.ContactDao;
import com.gdu.app09.dto.ContactDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor  // private final ContactDao contactDao;에 @Autowired를 하기 위한 코드이다.
@Service  // ContactService 타입의 객체(Bean)을 Spring Container에 저장한다.
public class ContactServiceImpl implements ContactService {

  private final ContactDao contactDao;
  
  @Override
  public int addContact(ContactDto contactDto) {
    int addResult = contactDao.insert(contactDto);
    return addResult;
  }

  @Override
  public int modifyContact(ContactDto contactDto) {
    int modifyResult = contactDao.update(contactDto);
    return modifyResult;
  }

  @Override
  public int deleteContact(int contact_no) {
    int deleteResult = contactDao.delete(contact_no);
    return deleteResult;
  }

  @Override
  public List<ContactDto> getContactList() {
    return contactDao.selectList();
  }

  @Override
  public ContactDto getContactByNo(int contact_no) {
    return contactDao.selectContactByNo(contact_no);
  }
}

Junit

작동이 잘되는지 test를 하는 것이다.
given-when-then 패턴으로 작성을 할 수 있으며 1개의 단위 테스트를 3가지 단계로 나누어 처리하는 패턴이다.

  • given(준비) : 어떠한 데이터가 준비되었을 때
  • when(실행): 어떠한 함수를 실행하면
  • then(검증): 어떠한 결과가 나와야 하는지
package com.gdu.app09;

import static org.junit.Assert.*;

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.gdu.app09.dao.ContactDao;
import com.gdu.app09.dto.ContactDto;

/*
 * JUnit 처리 방법
 * 1. spring-test dependency를 추가한다.
 * 2. @RunWith를 추가한다.
 * 3. @ContextConfiguration을 추가한다.
 *    ContactDao 객체(Bean)을 생성한 방법에 따라서 아래 3가지 방식 중 선택한다.
 *    1) <bean> 태그 : @ContextConfiguration(locations="file:src/main/webapp/WEB-INF/spring/root-context.xml")
 *    2) @Bean       : @ContextConfiguration(classes=AppConfig.class)
 *    3) @Component  : @ContextConfiguration(locations="file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")
 */

// JUnit4를 이용한다.
@RunWith(SpringJUnit4ClassRunner.class)

// ContactDao Bean 생성 방법을 알려준다.
@ContextConfiguration(locations="file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")

// 테스트 메소드의 이름 오름차순(알파벳순)으로 테스트를 수행한다.
@FixMethodOrder(MethodSorters.NAME_ASCENDING)

public class ContactUnitTest {

  @Autowired  // Spring Container에서 ContactDao 객체(Bean)를 가져온다.
  private ContactDao contactDao;
  
  @Test  // 테스트를 수행한다.
  public void test01_삽입테스트() {
    ContactDto contactDto = new ContactDto(0, "이름", "연락처", "이메일", "주소", "");
    int insertResult = contactDao.insert(contactDto);
    assertEquals(1, insertResult);  // insertResult가 1이면 테스트 성공이다.
  }
  
  @Test  // 테스트를 수행한다.
  public void test02_조회테스트() {
    int contact_no = 1;
    ContactDto contactDto = contactDao.selectContactByNo(contact_no);
    assertNotNull(contactDto);  // contactDto가 not null이면 테스트 성공이다.
  }
  
  @Test  // 테스트를 수행한다.
  public void test03_삭제테스트() {
    int contact_no = 1;
    int deleteResult = contactDao.delete(contact_no);
    assertEquals(1, deleteResult);  // deleteResult가 1이면 테스트 성공이다.
    // assertNull(contactDao.selectContactByNo(contact_no));  select 결과가 null이면 테스트 성공이다.
  }

}

Log4j

로그를 남기기 위한 장치로 관련된 프레임워크는 log4j, logback.log4j2 순으로 등장했으며 그리고 이를 통합하여서 인터페이스로 제공하는 SLF4J 라이브러리가 있다.
가장 널리 사용되고 있는 것은 logback 이며 slf4j의 구현체로써 동작하기때문에 boot에서는 기본적으로 포함되어 있어서 디펜던시의 추가없이 사용할 수 있다.
가장 최신에 나온 logging 프리임워크로는 log4j2로 log4j의 다음 버넞이다 logback처럼 필터링 기능과 자동리로딩을 지원한다. logback과는 가장 큰 차이는 멀티쓰레드 환경에서 비동기 로거의 경우 다른 logging 프레임워크보다는 처리량이 많고 대기시간이 짧다. 람다식을 지원한다.


log의 종류
1. error :사용자 요청을 처리하는 중 발생한 문제
2. warn : 처리 가능 한 문제이지만 향후 시스템 에러의 원인이 될 수 있는 문제
3. info : 로그인이나 상태 변경과 같은 정보성 메시지
4. debug :개발시 디버깅 목적으로 출하는 메시지
5. trace : 디버그보다 좀 상세한 메세지

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.gdu.app09">
		<level value="info" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0

Logback

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <!-- Appenders : 로그를 출력하는 Appender 모음 -->
  
  <!--
    %d{날짜시간패턴} : 로그기록시간 (SimpleDateFormat과 같은 날짜시간패턴)
    %level           : 로그 레벨 (OFF > ERROR > WARN > INFO > DEBUG > TRACE)
    %logger          : 로그를 찍는 클래스 (어떤 클래스가 동작할 때 로그가 남겨지는가?)
    %msg             : 로그 메시지
    %n               : 줄 바꿈
  -->
  
  <!-- Console에 로그를 찍는 Appender -->
  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>[%d{HH:mm:ss, Asia/Seoul}] %-5level:%logger - %msg%n</pattern>
    </encoder>
  </appender>
  
  <!-- File에 로그를 찍는 Appender -->
  <appender name="file" class="ch.qos.logback.core.FileAppender">
    <file>/log/app09_log.log</file>
    <append>true</append>
    <immediateFlush>true</immediateFlush>
    <encoder>
      <pattern>[%d{HH:mm:ss, Asia/Seoul}] %-5level:%logger - %msg%n</pattern>
    </encoder>
  </appender>
  
  <!-- 매일 새로운 로그 파일을 만드는 RollingFileAppender -->
  <appender name="rolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>/log/app09.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      <maxFileSize>100MB</maxFileSize>
      <maxHistory>30</maxHistory>
      <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
      <pattern>[%d{HH:mm:ss, Asia/Seoul}] %-5level:%logger - %msg%n</pattern>
    </encoder>
  </appender>
  
  <!-- Application Loggers -->
  <logger name="com.gdu.app09" level="info" />
  
  <!-- 3rdparty Loggers -->
  <logger name="org.springframework" level="info" />
  <logger name="log4jdbc"            level="info" />
  <logger name="jdbc.sqlonly"        level="info" />  <!-- 쿼리문 출력하기 -->
  <logger name="jdbc.sqltiming"      level="off" />   <!-- 쿼리문 + 실행시간 출력하기 -->
  <logger name="jdbc.resultsettable" level="info" />  <!-- SELECT 결과를 테이블 형식으로 출력하기 -->
  <logger name="jdbc.connection"     level="off" />   <!-- Connection 연결/종료 정보 출력하기 -->
  <logger name="jdbc.audit"          level="off" />   <!-- ResultSet을 제외한 jdbc 호출 정보 출력하기 -->
  <logger name="jdbc.resultset"      level="off" />   <!-- ResultSet을 포함한 jdbc 호출 정보 출력하기 -->
  
  <!-- Root Logger -->
  <root>
    <priority value="warn" />
    <appender-ref ref="console" />
    <appender-ref ref="rolling" />
  </root>
  
</configuration>

logback 클래스

package com.gdu.app10.logback;

import java.text.SimpleDateFormat;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MyLogbackLayout extends LayoutBase<ILoggingEvent> {

  @Override
  public String doLayout(ILoggingEvent event) {
    
    StringBuilder sb = new StringBuilder();
    
    sb.append("[");
    sb.append(new SimpleDateFormat("HH:mm:ss").format(event.getTimeStamp()));
    sb.append("] ");
    sb.append(String.format("%-5s", event.getLevel()));
    sb.append(":");
    String loggerName = event.getLoggerName();
    sb.append(loggerName);
    if(loggerName.equals("jdbc.sqlonly")) {
      sb.append("\n");
    } else {
      sb.append(" - ");
    }
    sb.append(event.getFormattedMessage());
    sb.append("\n");
    
    return sb.toString();    
  }  
}

출력양식을 만들어 주는 클래스다 .

profile
백엔드 준비생의 막 블로그

0개의 댓글