TODAY TOTAL
분류 전체보기 (78)
[Spring] 프로젝트 폴더 생성 시 패키지로 보이는 현상 해결하기

 

스프링 공부를 할때 가끔(?)  src/main/resources에 폴더 생성을 하거나 다른 곳에 폴더 생성을 할때

 

패키지로 나오는 버그가 있다. 밑에 방식대로 하면 해결 할 수 있다.

 

 

먼저 하고 있는 프로젝트 우클릭 후 Properties(속성) 선택

 

1. Java Build Path 선택 -> 위 탭 중 Source 클릭 -> ex02/src/main/resources 중 Excluded 선택

 

 

2. 선택 후 Edit 선택

 

3. Exclusion patterns에 추가를 해야한다 (위에 추가해도 해결 안됨) -> Add 클릭 후

 

** 입력하고 확인하면 해결 되어 있다!! (**을 입력하면 됨)

'Error' 카테고리의 다른 글

[Tomcat] 톰캣 45초 서버 오류 해결하기  (0) 2021.08.21
[Oracle]SQL Developer 11C -> 18C 변경 후 오류  (0) 2021.08.10
  Comments,     Trackbacks
[Spring] Mybatis와 스프링 페이징 처리

 

 

페이징 처리때 쓸 클래스 'Criteria'

 

전체목록을 페이지 별로 나눠서 보여줄려면, Criteria를 생성해야한다.

 

즉, 검색에 사용되는 여러 종류의 데이터를 하나의 객체로 묶기 위한 용도

 

 

@Getter 
@Setter
@ToString
public class Criteria {
 
   private int pageNum;
   private int amount;
   
   public Criteria() {
      this(1,10);
   }
   
   public Criteria(int pageNum, int amount) {
      this.pageNum = pageNum;
      this.amount = amount;
   }   
   
}

 

그리고 페이징을 할때는 조금의 산수가 필요하다...ㅠ

 

	private int curPage;          // 현재 페이지 번호
	private int rowSizePerPage;   // 한 페이지당 레코드 수
	private int totalPageCount;   // 총 페이지 건수
	private int pageSize;   // 페이지 리스트에서 보여줄 페이지 갯수
	
	
	 // --내부에서 계산 (getter) 왜? 사용자로부터 받는게 없기 때
	private int startRow ;        // 시작 레크드 번호   
	private int endRow;          // 마지막 레크드 번호 
	private int totalRowCount ;   // 총 레코드 건수
	
	
	private int startPage;  // 페이지 리스트에서 시작  페이지 번호 
	private int endPage;   // 페이지 리스트에서 마지막 페이지 번호

 

	/*
	 * 입력 받은 변수를 기반으로
	 * 페이징 관련 변수 계산 
	 */
	public void setting() {
		if(rowSizePerPage < 1) rowSizePerPage = 10;
		if(curPage < 1) curPage = 1;
		if(pageSize < 1) pageSize = 10;
		 
		 //totalPageCount = (int)Math.ceil(totalRowCount /rowSizePerPage); //현재 33이 되야 함
		 totalPageCount = (totalRowCount - 1) / rowSizePerPage + 1; //현재 33이 되야 함
		 System.out.println("totalPageCount = " +totalPageCount);
		 startRow = (curPage -1) * rowSizePerPage + 1;
		 //curPage * rowSizePerPage
		 endRow = startRow + rowSizePerPage - 1 ; // curPage + rowSizePerPage
		 System.out.println("curPage = " +curPage);
		 System.out.println("pageSize = " +pageSize);
		 
		 startPage = (curPage -1) /pageSize * pageSize + 1; //11
		 endPage = startPage + pageSize - 1 ; //15
		 if(endPage > totalPageCount) endPage = totalPageCount;
		 
    }

<페이징 계산을 적은 것>

 

 

 

<JSP 화면 번호 출력>

 

 <div class='pull-right'>
          <ul class="pagination">
 
            <c:if test="${pageMaker.prev}">
              <li class="paginate_button previous"><a href="#">Previous</a>
              </li>
            </c:if>
 
            <c:forEach var="num" begin="${pageMaker.startPage}"
              end="${pageMaker.endPage}">
              <li class="paginate_button"><a href="#">${num}</a></li>
            </c:forEach>
 
            <c:if test="${pageMaker.next}">
              <li class="paginate_button next"><a href="#">Next</a></li>
            </c:if>
          </ul>
        </div>
        <!--  end Pagination -->
      </div>

 

<수정/삭제 후 이동>

 

 @PostMapping("/remove")
  public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
 
    log.info("remove..." + bno);
    if (service.remove(bno)) {
      rttr.addFlashAttribute("result", "success");
    }
    rttr.addAttribute("pageNum", cri.getPageNum());
    rttr.addAttribute("amount", cri.getAmount());
 
    return "redirect:/board/list";
  }

 

 

 

제목 클릭 후 상세보기 그리고 수정페이지

 

 

  Comments,     Trackbacks
[Spring] 페이징 처리 시 쿼리문

 

 

오라클은 페이징 시 DB에 쿼리문으로 ROWNUM을 사용한다. 이게 은근 까다롭다.

 

ROWNUM의사 컬럼으로 참조만 될 뿐 데이터베이스에 저장되지 않습니다.

 

또한 SELECT절에 데이터(row)에 붙는 순번입니다.

 

그리고 ROWNUM과 ORDER BY를 같이 사용한다면 먼저 ROWNUM이 먼저 부여 되고

그다음으로 ORDER BY가 실행된다.

 

주로 <, <= 사용하며 >, >= 인 경우 ROWNUM가 실행되지 않습니다.

 

ROWNUM보다 작거나 큰건 말도 안되는 일

 

그리고 ORDER BY는 가끔적 사용하지 말자, 어쩔 수 없이 써야 된다면 쓸 수 있지만

 

속도를 저하 시키기 때문에 좋지는 않다.

 

지금은 데이터가 많이 없어서 ORDER BY를 쓰든 안쓰든 별 차이가 없지만 데이터가 수만개가 된다면 

1초나 2초가 차이가 나버린다. 

 

이럴경우 고객을 비롯해 선임에게 재떨이를 맞을 수 있다.

 

 

인덱스(Index)란?

인덱스는 데이터베이스 테이블에 있는 데이터를 빨리 찾기 위한 용도의 데이터베이스 객체이며 일종의 색인기술입니다. 테이블에 index를 생성하게 되면 index Table을 생성해 관리합니다. 인덱스는 테이블에 있는 하나이상의 컬럼으로 만들 수 있습니다. 가장 일반적인 B-tree 인덱스는 인덱스 키(인덱스로 만들 테이블의 컬럼 값)와 이 키에 해당하는 컬럼 값을 가진 테이블의 로우가 저장된 주소 값으로 구성됩니다.

 

 

 

ORDER BY 대신 INDEX를 쓰는 것이 좋다.

 

 

SELECT절에 주석같이 생긴게 있다. 저건 힌트라는 것인데

 

힌트란? 

일종의 지시 구문인데, 오라클이 항상 최적의 실행 경로를 만들어 내기는 불가능하기 때문에

직접 최적의 실행경로를 작성해주는 것이다.

 

  • 힌트를 사용하여 아래와 같은 것들을 할 수 있다.
  • 액세스 경로, 조인 순서, 병렬 및 직렬 처리, Optimizer의 목표(Goal)를 변경 가능하다.
  • 데이터 값을 정렬해야 하는 경우, 힌트의 사용이 필요하다.
  • 또한, 드라이빙 테이블을 원하는 대로 선정하고자 할 때도 사용된다.

 

일단 몇가지만 소개

 

 

/*+ FULL(table_name) */

 

:: Table을 모두 스캔 하길 원할 때 사용합니다.

 

 

/*+ INDEX(table_name index_name) */


::   지정된 index를 강제적으로 쓰게끔 지정
  - in list predicat에 대해서도 가능.
  - Multi-column inlists는 index를 사용할 수 없다.

 

 

 

 

 

 

 

 

:: 참고한 블로그

https://coding-factory.tistory.com/419

  Comments,     Trackbacks
[JSP] Log4j, slf4j 설치

 

https://www.apache.org/

 

Welcome to The Apache Software Foundation!

Official ASF reports and statements, including Quarterly and Annual Reports, Vision Statement, "Apache is Open", 5-Year Strategic Plan, and more.

www.apache.org

 

사이트 클릭

 

메뉴바 중 첫번째 Projects 클릭하면

리스트가 쭉 나오는데

Projects List 클릭

 

 

 

알파벳 순으로 나오는데

L 끝 쪽에 'Logging Services' 클릭

 

 

많은 Log4j 중에

 

첫번째 Apache Log4j 

Project site 클릭

 

 

 

내리다 보면 왼쪽 카테고리에 LEGACT SITES에

Log4j1.2 - End of Life 클릭

 

(1.2가 오래되긴 해도 많이 써서 1.2로 다운받는게 좋은듯 하다)

 

클릭하면

 

Download 클릭

 

 

 

tar.gz 는 리눅스

zip은 윈도우

 

zar파일을 라이브러리 폴더에 넣으면 끝!

 

 


끝이 아니라 slf4j 설치하기

 

https://mvnrepository.com/

maven 홈페이지 검색창에 ' slf4j' 치면

 

SLF4J API Module 클릭

 

 

 

제일 밑에 있는 1.7.32 다운

 

 

 

Files에 있는 jar 다운

 

위에 처럼 똑같이 라이브러리 폴더에 넣으면 된다.

 

밑에 있는 코드는 Maven 할 시 jar 파일 대신 코드를 넣는다.

 

 

근데 여기서 이것만 넣을 경우 로그가 잘 안나오는 경우가 있어서 

 

slf4j 홈페이지에 가서 다른 jar 파일을 받아야 한다.

 

 

http://www.slf4j.org/

 

SLF4J

Simple Logging Facade for Java (SLF4J) The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framewor

www.slf4j.org

 

들어가서 

 

Download 클릭

 

 

가운데 잘 찾아보면 Maven central 클릭

 

 

여기서 slf4j-api/ 

클릭

 

 

1.7.32/

클릭

 

 

slf4j-api-1.7.32.jar

 

 

라이브러리에 넣으면 된다.

 

slf4j-api-1.7.32.jar

slf4j-log4j12-1.7.32.jar

 

이 두개가 있어야 로그 잘 됨

'Back-End > JSP' 카테고리의 다른 글

[JSP] MyBatis Mapper,Log4j,싱글톤 패턴 써보기  (0) 2021.07.23
[JSP] 디자인 패턴 및 SOLID 원칙  (0) 2021.07.22
[JSP] 페이징  (0) 2021.07.22
[JSP] 회원 정보 만들기-3  (0) 2021.07.20
[JSP] 회원 정보 만들기-2  (0) 2021.07.20
  Comments,     Trackbacks
[정보처리기사] 8. 제품 소프트웨어 패키징

애플리케이션 패키징(Application Packaging)개념

 

개발이 완료된 제품 소프트웨어를 배포,설치할 수 있도록 고객에 전달

설치와 사용에 필요한 제반 내용을 포함하는 매뉴얼을 작성하는 활동이다.

 

 

패키징 특징

 

버전관리 및 릴리즈 노트를 통해 지속적으로 관리한다.

 

 

사용자 관점에서의 패키징 고려사항

 

경정의

UI제공

리서비스

패키징의

환유관병 -> 환관 유비를 체포하기 위하여 관병이 출동했다.

 

 

애플리케이션 배포 도구의 기술요소

 

호화

관리

별 기술

작권 표현

호화 파일 생성

책관리

랙 방지

 

암키식저 파정크인

 

:: 역공학 : 기존 개발된 시스템의 기술적인 원리를

             시스템의 코드나 데이터등의 구조분석을

             통해 도출해 내는 작업이다.

 

애플리케이션 배포 도구 활용 시 고려사항

 

호화/보안

기종 연동

잡성 및 비효율성 문제

적합 암호화 알고리즘 적용

 

암이복최 ->암표를 팔다 만난 이복형제 최씨

 

 

 

DRM(Digital Rights Management)의 개념

 

정보보호 기술 중 하나로 암호화 기술을 이용해서

비허가 사용자로부터 디지털 컨텐츠를 보호하게

하는 기술을 말한다.

 

 

DRM은 콘텐츠 제공자와 콘텐츠 소비자, 클리어링 하우스로 구성된다.

  Comments,     Trackbacks
[JSP] MyBatis Mapper,Log4j,싱글톤 패턴 써보기

학원에 다니면서 전에 만들었던 자유게시판, 회원목록, 페이징을 만들었는데, 오늘부터 xml파일을 이용하고 mybatis 를 이용하니 좀 더 간결하고 쉽게 만들었지는거 같다.

 

전에는 DaoOracle에 append로 쿼리를 싹다 붙이고 코드줄이 엄청 길었는데, 오늘 MyBatis퍼시스턴스 프레임워크를 쓰니 세상 편하다. 쓰는 이유가 다 있구나

 

처음에는 View단에 싹다 JDBC나 이것저것 놨다가 두번째는 DaoOracle,Service로 나눠서 서버단과 뷰단을 나누고 하니

원리를 이해 할 수 있어서 좋았다. 마지막은 최종 스프링인데, 뭔가 엄청 어려워 보이지만 끝까지 해보자!

 

 

https://mybatis.org/mybatis-3/ko/index.html

 

MyBatis – 마이바티스 3 | 소개

Copyright © 2009–2021MyBatis.org. .

mybatis.org

홈페이지에 시작하기를 눌러 천천히 따라하기.

 

 

홈페이지가 한글번역이 된 MyBatis.

 

메이븐을 지금은 사용하지 않기 때문에 

메뉴상단바 Help-Eclipse Marketplace에서

'MyBatipse' 라고 치면 

 

install 후 약관 동의 후 완료 클릭

 

 

 

전에 만들었던 자유게시판을 수정도 하고, mapper,xml 파일을 추가할 것이다.

 

전에는 Dao에서 만들었던 것이 Mapper로 바뀌는 것이다. 즉, DB에 접근해서 데이터를 조회하고 조작

하는 기능 담당했던 객체 Dao가 Mapper로 바뀌는 것이다.

 

 

그러면 Mapper는 무엇이길래 Dao에서 갑자기 바꾸는 것일까?

 

바꾸면 다음과 같이 좋은 점이 있다.

 

  • SQLSession을 등록 하지 않아도 된다.
  • DAO인터페이스와 인터페이스를 구현한 DAO클래스를 생성 안해도 된다.
  • 문자열을 작성할때 실수가 줄어든다.
  • IDE에서 제공하는 Code assist를 사용할 수 있다.

 

그리고 싱글톤 패턴이 있다.

 

오직 하나의 인스턴스만 사용할 수 있도록 객체를 생성하는 방법이다.

여기서 인스턴스는 클래스를 복제 해 쉽게 관리해주는 게 인스턴스다.

 

 

FreeBoardServiceImpl.java

 

package com.study.free.service;
	
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.study.exception.BizNotFoundException;
import com.study.exception.BizPasswordNotMatchedException;
import com.study.free.vo.FreeBoardSearchVO;
import com.study.free.vo.FreeBoardVO;
import com.study.util.MybatisSqlSessionFactory;
	
public class FreeBoardServiceImpl implements IFreeBoardService {
	
	//싱글톤 패턴
	
//	오직 하나의 인스턴스만 사용할 수 있도록 객체를 생성하는 방법이다.
//	최초 하나의(Single) 인스턴스만 생성하고, 이후에는 이 인스턴스를 참조하게 된다.
//	정적인 클래스 멤버변수 선언 ( - private), 관례적으로 변수명은 instance
//	생성자 숨김(private) <- 외부에서 new 생성 금지 내부에서만 호출 가능 
//	new를 통해서 가져가라
//	정적멤버변수를 제공(return)하는 공개 매서드 생성, 관레적으로 메서드명은 getInstance()
	private static FreeBoardServiceImpl instance = new FreeBoardServiceImpl(); //내부에서 호출가능 new FreeBoardServiceImpl();는 자기자신
	//생성된 인스턴스를 넘겨줘야하고, public으로 이동
	private FreeBoardServiceImpl () {
		//생성자는 막아줘야한다. 외부에서 생성 못하게
	}
	//생성된 인스턴스 도착.
	public static FreeBoardServiceImpl getInstance() { //FreeBoardServiceImpl.getInstance
		if(instance == null) instance = new FreeBoardServiceImpl();
		return instance;
	}
	
	private SqlSessionFactory factory = MybatisSqlSessionFactory.getSqlSessionFactory(); 
	
	
	//private String JDBC_URL = "jdbc:apache:commons:dbcp:study";
	//private IFreeBoardDao boardDao = new FreeBoardDaoOracle();				 //
	
	@Override
	public int getBoardCount(FreeBoardSearchVO searchVO) {

		try(SqlSession session = factory.openSession()) {
			//insert() , update(), delect(), selectOne(), selectList(), selectMap()
			int tot = session.selectOne("com.study.free.dao.IFreeBoardDao.getBoardCount",searchVO);
			
			return tot;
			
		} 
		
	}
	
	@Override
	public List<FreeBoardVO> getBoardList(FreeBoardSearchVO searchVO) {

		try(SqlSession session = factory.openSession()) {
			//insert() , update(), delect(), selectOne(), selectList(), selectMap()
			List<FreeBoardVO> list = session.selectList("com.study.free.dao.IFreeBoardDao.getBoardList",searchVO);
			
			return list;
			
		} 
		
		
	}

	@Override
	public FreeBoardVO getBoard(int boNo) throws BizNotFoundException {
		
		try(SqlSession session = factory.openSession()) {
			FreeBoardVO Board = session.selectOne(
									"com.study.free.dao.IFreeBoardDao.getBoard",boNo);
			if(Board == null) {
				throw new BizNotFoundException("글번호 ["+boNo + "] 조회 실패");
			} 
			return Board;
			
		} 
		
		
//		Connection conn = null;
//		try {
//			conn = DriverManager.getConnection(JDBC_URL);
//			
//			FreeBoardVO Board = boardDao.getBoard(boNo);
//			//Board가 널인 경우 BizNotFoundException 예외발생
//			if(Board == null) {
//				throw new BizNotFoundException("글번호 ["+boNo + "] 조회 실패");
//			} 
//			return Board;
//		} catch (SQLException e) {
//			e.printStackTrace();
//			throw new RuntimeException(e.getMessage(), e);
//		}finally {
//			if (conn != null) try {conn.close();} catch (SQLException e) {}
//		}
	}

	@Override
	public void registBoard(FreeBoardVO board) {
//		Connection conn = null;
//		try {
//			conn = DriverManager.getConnection(JDBC_URL);
//			conn.setAutoCommit(false);
//			
//			boardDao.insertBoard(board);
//			
//			conn.commit();
//		}catch (SQLException e) {
//			e.printStackTrace();
//			try {
//				conn.rollback();
//			}catch (SQLException e2) {}
//			throw new RuntimeException(e.getMessage(),e);
//		}finally {
//			if (conn != null) try {conn.close();
//			}catch (SQLException e) {}
//		}
		
	}

	@Override
	public void modifyBoard(FreeBoardVO board) 
			throws BizNotFoundException, BizPasswordNotMatchedException {
//		Connection conn = null;
//		try {
//			conn = DriverManager.getConnection(JDBC_URL);
//			conn.setAutoCommit(false);
//			
//			FreeBoardVO vo = boardDao.getBoard(board.getBoNo());
//			if(vo == null) {
//				throw new BizNotFoundException("글 번호 [" + board.getBoNo() + "] 글 조회 실패");
//			}
//			if( !vo.getBoPass().equals(board.getBoPass() ) ) {
//				throw new BizPasswordNotMatchedException("패스워드가 다릅니다.");
//			}
//			
//			boardDao.insertBoard(board);
//			
//			conn.commit();
//		}catch (SQLException e) {
//			e.printStackTrace();
//			try {
//				conn.rollback();
//			}catch (SQLException e2) {}
//			throw new RuntimeException(e.getMessage(),e);
//		}finally {
//			if (conn != null) try {conn.close();
//			}catch (SQLException e) {}
//		}
		
	}

	@Override
	public void removeBoard(FreeBoardVO board) 
			throws BizNotFoundException, BizPasswordNotMatchedException {
	//제거
//		Connection conn = null;
//		try {
//			conn = DriverManager.getConnection(JDBC_URL);
//			conn.setAutoCommit(false);
//			//비밀번호 확인
//			FreeBoardVO vo = boardDao.getBoard(board.getBoNo());
//			if(vo == null) {
//				throw new BizNotFoundException("글 번호 [" + board.getBoNo() + "] 글 조회 실패");
//			}
//			if( !vo.getBoPass().equals(board.getBoPass() ) ) {
//				throw new BizPasswordNotMatchedException("패스워드가 다릅니다.");
//			}
//			//비밀번호 확인
//			
//			boardDao.deleteBoard(board);
//			// TODO cnt가 0 인 경우 BizNotEffectedException 예외 발생
//			
//			conn.commit();
//		} catch (SQLException e) {
//			e.printStackTrace();
//			try {conn.rollback();} catch (SQLException e2) {}
//			throw new RuntimeException(e.getMessage(), e);
//		}finally {
//			if (conn != null) try {conn.close();} catch (SQLException e) {}
//		}		
		
	}

	@Override
	public void increaseHit(int boNo) {
//		Connection conn = null;
//		try {
//			conn = DriverManager.getConnection(JDBC_URL);
//			conn.setAutoCommit(false);
//			
//			boardDao.increaseHit(boNo);
//			
//			conn.commit();
//		} catch (SQLException e) {
//			e.printStackTrace();
//			try {conn.rollback();} catch (SQLException e2) {}
//			throw new RuntimeException(e.getMessage(), e);
//		}finally {
//			if (conn != null) try {conn.close();} catch (SQLException e) {}
//		}	
		
		
		
	}

}

:: 주석 단게 전에 했던 Dao

 

이렇게 바꿨다면 이제 List,Edit,View,Delete,Modify,Regist에 getInstance를 넣어줘야한다.

전에는 객체를 계속 생성해서 바뀌는데, 오직하나의 인스턴스만 생성하기 때문에 바뀌지 않는다.

 

최초 하나의(Single) 인스턴스만 생성하고, 이후에는 이 인스턴스를 참조하게 된다.

정적인 클래스 멤버변수 선언 ( - private), 관례적으로 변수명은 instance

 

ICodeService codeService = new CodeServiceImpl(); (전)

 

ICodeService codeService = CodeServiceImpl.getInstance(); (후)

 

(코드 주석 참조)

 


WEB-INF/lib에 넣어놨다.

 

 

 

log4j.xml

 

log4j는 아파치에서 만든 오픈소스 라이브러리로써 로그를 남기 위해 사용되는 자바기반 로깅 유틸리티입니다.

디버그용 도구로 사용합니다.

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration 
		PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" 
		"http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<!--
  ** 1. jar : log4j-1.2.17.jar
     2. log4.xml 파일은 /WEB-INF/classes 바로 존재해야함    
     		maven인 경우 resource폴더 바로 밑에
     		일반 웹어플인 경우 src 폴더 밑에
     
 -->
 
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  
<!--  콘솔 로그   -->
<appender name="console" class="org.apache.log4j.ConsoleAppender"> 
    <layout class="org.apache.log4j.PatternLayout"> 
        <param name="ConversionPattern" value="[%-5p] %t %c.%M:%L - %m %n "/> 
    </layout> 
</appender>
  
<!--  날짜별 로그 --> 
<appender name="dailyFile" class="org.apache.log4j.DailyRollingFileAppender">
    <!-- 이것은 날짜별로  로그를 남김. 파일명.확장자.DatePattern으로 정의 함--> 
     <param name="File" value="${user.home}/log/study3.log" />

    <param name="Append" value="true" />
    <param name="Encoding" value="UTF-8" />
    <!-- param name="MaxFileSize" value="100000KB" /> -->
    <!-- param name="MaxBackupIndex" value="10" /> -->
    <param name="DatePattern" value="'.'yyMMdd"/>  
    <!--yyMMddHHmm, 시간별로 하고싶다 HH, 일별로 하고 싶다 ddd  -->
    <layout class="org.apache.log4j.PatternLayout"> 
        <param name="ConversionPattern" value="[%-5p] %t %c.%M:%L - %m %n"/>
         
         <!--
          <param name="ConversionPattern" value="%t> 
              [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n"/>
           위 pattern 결과 :http-bio-8080-exec-6> [2017-01-12 12:07:44] [root] [179] [ERROR]
         --> 
    </layout> 
</appender>     
 
<!-- xml 로그--> 
<!-- xml형식으로 로그를 남김 
<appender name="xmlout" class="org.apache.log4j.FileAppender"> 
    <param name="file" value="log4jxml.xml"/> 
    <param name="Append" value="true"/> 
    <layout class="org.apache.log4j.xml.XMLLayout"/> 
</appender> 
 -->
 
<!--  팩키지별 로그 설정 -->
<logger name="org.springframework" additivity="false">
    <level value="WARN" />
    <appender-ref ref="dailyFile" />
    <appender-ref ref="console" />
</logger>

<logger name="com.study" additivity="false">
    <level value="debug" />
    
</logger>

 
<!--
  root 로거 기본 설정  
    - ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF
    
    DEBUG : 개발자가 변수들 확인하기 위한 용도
    INFO  : 개발자가 중요한 변수 확인하기 위한 용도
    WARN  : 에러는 아니지만 뭔가 의심스러운 변수를 확인하기 위한 용도
    ERROR : 오류가 발생한 경우, 대단히 의심스러운 경우 확인
    FATAL : 애플리케이션 운영에 심각한 영향이 있을 경우 FATAL로 로그를 남긴다. 
-->
<root>
  <level value="info" />
  <appender-ref ref="console"/>
  <appender-ref ref="dailyFile"/>
</root>
 
</log4j:configuration>

 

root 로거 기본 설정  
    - ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF
    
    DEBUG : 개발자가 변수들 확인하기 위한 용도
    INFO  : 개발자가 중요한 변수 확인하기 위한 용도
    WARN  : 에러는 아니지만 뭔가 의심스러운 변수를 확인하기 위한 용도
    ERROR : 오류가 발생한 경우, 대단히 의심스러운 경우 확인
    FATAL : 애플리케이션 운영에 심각한 영향이 있을 경우 FATAL로 로그를 남긴다. 

 

ALL이나 TRACE는 사용을 잘 안하고 DEBUG부터 5단계 보통 시작한다.

 

 

 

app_config.properties

 

#config/ app_config.properties


jdbc.driverClassName =oracle.jdbc.driver.OracleDriver
jdbc.url =jdbc:oracle:thin:@127.0.0.1:1521:xe
jdbc.user =java
jdbc.password =사용하는 DB 비밀번호 입력

 

mybatis-config.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

	<properties resource="config/app_config.properties" />

	<settings>
			<!-- table : bo_no vo : boNo 
			전통적인 데이터베이스 칼럼명 형태인
			A_COLUMN을 CamelCase형태의 자바 프로퍼티명 형태인
			aColumn으로 자동으로 매핑하도록 함
			-->
		 <setting name="mapUnderscoreToCamelCase" value="true" />
		
		<!-- JDBC타입을 파라미터에 제공하지 않을때 null값을 처리한 JDBC타입을 명시한다.
		     일부 드라이버는 칼럼의 JDBC타입을 정의하도록 요구하지만
		     대부분은 NULL, VARCHAR 나 OTHER 처럼 일반적인 값을 사용해서 동작한다. -->
		<setting name="jdbcTypeForNull" value="NULL" />
		<!-- 가져온 값이 null일 때 setter나 맵의 put 메소드를 호출할지를 명시
		 Map.keySet() 이나 null값을 초기화할때 유용하다.
		  int, boolean 등과 같은 원시타입은 null을 설정할 수 없다는 점은 알아두면 좋다. -->
		<setting name="callSettersOnNulls" value="true" />
	
	</settings>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>
  
  
  <!-- 작성한 매퍼파일 등록 
  		스프링 연동시 "*" 사용가능 mybatis/mapper/*.xml
  -->
  <mappers>
    <mapper resource="mybatis/mapper/freeboard.xml"/>
  </mappers>
</configuration>

app_config.properties에 있는 jdbc 프로퍼티를 가져온다.

 

 

 

freeboard.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="com.study.free.dao.IFreeBoardDao">
	
 	<select id="getBoardCount" 
 	resultType="int">
 		SELECT COUNT(*) AS tot 
		 FROM free_board         
		WHERE bo_del_yn ='N'   
 	</select>
	
	<select id="getBoardList" 
	resultType="com.study.free.vo.FreeBoardVO">
 		SELECT bo_no   	
				    , bo_title    
				    , bo_category 
					 ,(SELECT comm_nm from comm_code WHERE comm_cd = bo_category ) as bo_category_nm 
				    , bo_writer  
				    , bo_content 
				    , bo_pass    
				    , bo_ip      
				    , bo_hit     
				    , TO_CHAR(bo_reg_date, 'YYYY-MM-DD') AS bo_reg_date  
				    , TO_CHAR(bo_mod_date, 'YYYY-MM-DD') AS bo_mod_date  
				    , bo_del_yn    
				 FROM free_board   
				 WHERE bo_del_yn = 'N'
				 ORDER BY bo_no DESC 
 	</select>
 	<!-- 프리미티브형(기본형)은 변수 자체에 값을 보유하기 때문에, 이름은 아무렇게해도 되지만
 			의미 있게 짓는게 좋다. -->
 	<select id="getBoard" 
 			parameterType="int" 
 			resultType="com.study.free.vo.FreeBoardVO">
 		SELECT bo_no  
		    , bo_title  
		    , bo_category   
			 ,(SELECT comm_nm from comm_code WHERE comm_cd = bo_category ) as bo_category_nm 
		    , bo_writer  
		    , bo_content  
		    , bo_pass  
		    , bo_ip  
		    , bo_hit  
		    , TO_CHAR(bo_reg_date, 'YYYY-MM-DD-HH24:MI:SS') AS bo_reg_date   
		    , TO_CHAR(bo_mod_date, 'YYYY-MM-DD-HH24:MI:SS') AS bo_mod_date   
		    , bo_del_yn  
		 FROM free_board   
		 WHERE bo_no = #{티스토리짱} 
 	</select>
 	
 	<insert id="insertBoard" parameterType="com.study.free.vo.FreeBoardVO">
 					INSERT INTO free_board (
				      bo_no        
				    , bo_title   
				    , bo_category
				    , bo_writer  
				    , bo_pass    
				    , bo_content 
				    , bo_ip      
				    , bo_hit     
				    , bo_reg_date
				    , bo_del_yn  
				) VALUES (      
				<![CDATA[ 
				      seq_free_board.nextval
				    , #{boTitle}
				    , #{boCategory} 
				    , #{boWriter} 
				    , #{boPass} 
				    , #{boContent} 
				    , #{boIp} 
				     , 0 
				    , SYSDATE 
					 , 'N' ]]>)      
 		
 	</insert>
 	
 	<update id="updateBoard" parameterType="com.study.free.vo.FreeBoardVO">
 		<![CDATA[ 
 		UPDATE free_board 
		 SET bo_title = #{boTitle}        
		    , bo_category = #{boCategory}    
		    , bo_writer = #{boWriter}    
		    , bo_content = #{boContent}   
		    , bo_ip  = #{boIp}    
		    , bo_mod_date = SYSDATE     
		      where bo_no = #{boNo}
 		]]>
 	</update>
 	
 	<delete id="deleteBoard">
 	<![CDATA[
 	 UPDATE free_board
					SET  bo_del_yn = 'Y'    
					WHERE	bo_no = #{boNo}    
 	]]>
 	</delete>
 	
 	<update id="increaseHit" >
 	<![CDATA[
 			UPDATE free_board 
		  	SET bo_Hit = bo_hit + 1   
			WHERE	bo_no = #{은하철도999}    
 	]]>
 	</update>
 	
	
</mapper>

 

![CDATA] :

 

태그 안에서는 전부 문자열로 치환 합니다.

쿼리를 작성 할때 >, < 를 사용하게 되는데 닫힘 태그로 인식하기 때문에 조심해야합니다.

 

예를 들어

 

<select id = "test" resultMap="testMap">


	select test_id
    
    from supertest
    
    where salary > 99
    
</select>

이런 경우 에러가 나는데 그 이유는 where절에 있는 '>'가 닫힘 태그로 인식하기 때문입니다.

 

그래서

 

<select id = "test" resultMap="testMap">

	<![CDATA[
	select test_id
    
    from supertest
    
    where salary > 99
    ]]>
</select>

이렇게 쓰면 문자열로 인식하기 때문에 실행이 됩니다.

 

 

'Back-End > JSP' 카테고리의 다른 글

[JSP] Log4j, slf4j 설치  (0) 2021.07.23
[JSP] 디자인 패턴 및 SOLID 원칙  (0) 2021.07.22
[JSP] 페이징  (0) 2021.07.22
[JSP] 회원 정보 만들기-3  (0) 2021.07.20
[JSP] 회원 정보 만들기-2  (0) 2021.07.20
  Comments,     Trackbacks
[JSP] 디자인 패턴 및 SOLID 원칙

 

디자인 패턴(건축쪽에서 사용하는 용어)

 

 

이름의 중요성.

디자인 패턴이란 기존 환경 내에서 반복적으로 일어나는 문제들을 어떻게 풀어나갈 것인가에 대한 일종의 솔루션 같은 것입니다.
디자인 패턴 계의 교과서로 불리는 [GoF의 디자인패턴]에서는 객체지향적 디자인 패턴의 카테고리를



  "생성 패턴(Creational Pattern)"
  - 객체를 생성하는데 관련된 패턴들
    객체가 생성되는 과정의 유연성을 높이고 코드의 유지를 쉽게 함
    
, "구조 패턴(Structural Pattern)"
  - 프로그램 구조에 관련된 패턴들
    프로그램 내의 자료구조나 인터페이스 구조 등 프로그램의 구조를 설계하는데 활용할 수 있는 패턴들
    
, "행동 패턴(Behavioral Pattern)" 
  - 반복적으로 사용되는 객체들의 상호작용을 패턴화 해놓은 것들
  



3가지로 구분하고 있습니다.


객체지향 소프트웨어를 '잘' 설계한다는 것은 쉬운 일이 아니다.
게다가, 재사용할 수 있는 객체지향 소프트웨어를 만드는 것은 더 힘들기 때문에
설계를 할 때에는 지금 당장 갖고 있는 문제를 해결할 수 있어야 
하지만, 나중에 생길 수 있는 문제나 추가된 요구 사항들도 수용할 수 있도록 일반적이고 포괄적이어야 합니다.

이를 위해 설계할 때 고려사항으로 SOLID 원칙 등 객체지향적 소프트웨어 설계 방법론이 있고, 현업에서는 이에 따라 개발하기 위해 노력하고 있습니다.
개발의 경험이 쌓이다 보면 자신들이 전에 사용했던 코드와 유사한 기능을 구현해야 해서 이전의 코드를 들여다보는 경험을 종종 하게 됩니다.

그러다 전에 사용했던 해결책을 그대로 반복해서 사용하기도 하고, 변형해서 쓰기도 하고, 혹은 상황에 맞지 않다고 판단하여 다른 방향의 구현을 고민하기도 합니다.

디자인 패턴은 설계자로 하여금 재사용이 가능한 설계는 선택하고, 재사용을 방해하는 설계는 배제하도록 도와줍니다.
또한 패턴을 쓰면 이미 만든 시스템의 유지보수나 문서화도 개선할 수 있고, 클래스의 명세도 정확하게 할 수 있으며, 객체 간의 상호작용 또는 설계 의도까지 명확하게 정의할 수 있습니다.
간단하게 말해서 디자인 패턴은 설계자들이 "올바른" 설계를 "빨리" 만들 수 있도록 도와줍니다.

 

 


SOLID 원칙

 

S : SRP(Single Responsiblity Principle 단일 책임 원칙)

 - 소프트웨어의 설계 부품(클래스, 함수 등)은 단 하나의 책임만을 가져야 한다.
 - 응집도는 높고 결합도는 낮은 프로그램을 뜻한다.
 - 하나의 클래스, 메서드에 많은 기능을 넣지 말고, 하나의 기능에 집중
   그렇지 않다면 유지보수 시 너무 힘들다.

 

 

 

O : Open-Closed Principle (개방-패쇄 원칙)


 - 기존의 코드를 변경하지 않고(Closed) 기능을 수정하거나 추가할 수 있도록(Open) 설계해야 한다.
   OCP에 만족하는 설계를 할 때 변경되는 것이 무엇인지에 초점을 맞춘다.
   자주 변경되는 내용은 수정하기 쉽게 설계 하고, 
   변경되지 않아야 하는 것은 수정되는 내용에 영향을 받지 않게 하는 것이 포인트다. 
   이를 위해 자주 사용되는 문법이 인터페이스(Interface)이다.
   유지보수 비용을 줄여주고 코드의 가독성 또한 높아지는 효과를 얻을 수 있다.

 

 

L : Liskov Substitution Principle (리스코프 치환 원칙)
 
 - 자식 클래스는 부모클래스에서 가능한 행위를 수행할 수 있어야 한다.  - 리스코프 치환 원칙은 MIT 컴퓨터 사이

   언스 교수인 리스코프가 제안한 설계 원칙이다.
   부모 클래스와 자식 클래스 사이의 행위에는 일관성이 있어야 한다는 원칙이며,
   이는 객체 지향 프로그래밍에서 부모 클래스의 인스턴스 대신 자식 클래스의 인스터스를 사용해도 문제가 없어야

   한다는 것을 의미한다.

 

 

 

I : Interface Segregation Principle (인터페이스 분리 원칙)

  - 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다. 하나의 일반적인 인터페이스보다는,

    여러개의 구체적인 인터페이스가 낫다.
  - 우리는 스마트폰으로 전화, 웹서핑, 사진 촬영 등 다양한 기능을 사용할 수 있다. 
    그런데 전화를 할 때에는 웹서핑, 사진촬영 등 다른 기능은 사용하지 않는다. 
    따라서 전화기능과 웹서핑 기능 사진 촬영 기능은 각각 독립된 인터페이스로 구현하여,
    서로에게 영향을 받지 않도록 설계해야 한다. 
    
  - 이렇게 설계된 소프트웨어는 인터페이스 분리 원칙을 통해 시스템의 내부 의존성을 약화시켜 리팩토링, 수정,

    재배포를 쉽게 할 수 있다.

 

 

D : Dependency Inversion Principle (의존 역전 원칙)

  - 의존 관계를 맺을 때, 변화하기 쉬운것 보단 변화하기 어려운 것에 의존해야 한다는 원칙이다. 
  - 변화하기 어려운 것이란 추상적인 것을 말한다. 
    객체지향적인 관점에서 보자면 변화하기 쉬운것이란 구체화 된 클래스를 의미하고, 변화하기 어려운 것은

    추상클래스나 인터페이스를 의미한다

  - 의존관계를 맺을 때, 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺는다는 것을 의미한다.

 

'Back-End > JSP' 카테고리의 다른 글

[JSP] Log4j, slf4j 설치  (0) 2021.07.23
[JSP] MyBatis Mapper,Log4j,싱글톤 패턴 써보기  (0) 2021.07.23
[JSP] 페이징  (0) 2021.07.22
[JSP] 회원 정보 만들기-3  (0) 2021.07.20
[JSP] 회원 정보 만들기-2  (0) 2021.07.20
  Comments,     Trackbacks
[Vue] 기초-2

 

v-for-exer.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        /* 화면에 렌더링 할 때 사용할 제목의 색상을 파란색으로 정의 */
        .blue_style {color: blue}
    </style>
</head>
<body>
    <div id="app">
        <h1>윤고랭이가 좋아하는 과일은?</h1>
        <ul>
            <!-- v-for를 통해서 반복문으로 aFruits 과일 배열 데이터를 가져옴 -->
            <li v-for="(items, index) in aPersons">
                <!-- aFruits 안의 항목을 하나씩 꺼내서 HTML로 렌더링 -->
                <p>번호 : {{ index }}</p>
                <p>이름 : {{ items.name }}</p>
                <p>나이 : {{ items.age }}</p>

            </li>
        </ul>
    </div>
    <script>
        Vue.config.devtools=true;
        //F12 개발자모드 출력
    var app = new Vue({
        el : '#app',
        data : {
            aPersons: [
                {
                    name : '홍길동',
                    age : 27
                },
                {
                    name : '이순신',
                    age : 30
                },
                {
                    name : '강감찬',
                    age : 35
                }
            ]
        }
    });
    </script>

</body>
</html>

for...in:

반복문은 객체의 모든 열거 가능한 속성애 대해 반복

기본적인 for문과는 다르지만, 프로퍼티를 순회 할 수 있도록 합니다.

 

루프마다 객체의 열거할 수 있는 프로퍼티의 이름을 지정된 변수에 대입

 

 

for(변수 in 객체) {

       객체의 모든 열거할 수 있는 프로퍼티의 개수만큼 반복적으로 실행하고자 하는 실행문;

}

 

<li v-for="(items, index) in aPersons">

items,index는 변수이고 aPersons라는 객체

 

 

::결과화면

 

프로퍼티 개수만큼 실행됨.


 

computed.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        /* 화면에 렌더링 할 때 사용할 제목의 색상을 파란색으로 정의 */
        .blue_style {color: blue}
    </style>
</head>
<body>
    <div id="app">
        <p>원본 문장 : "{{ sOriginalMessage }}"</p>
        <p>대문자 변환 문장 : "{{ fnUpperCaseMsg }}"</p>
    </div>
    <script>
        Vue.config.devtools=true;
    new Vue({
        el : '#app',
        //data는 메스태시 안에 넣을 값이 간단할 때 사용
        data : {
            sOriginalMessage : 'How are you?'
        },
        // computed는 머스태시 안에 넣을 JS 로직이 복잡할 때 사용
        computed: {
            fnUpperCaseMsg: function() {
                return this.sOriginalMessage.toUpperCase();
            }
        }

        // computed 속성
        // 머스태시에 작성할 로직이 복잡하면 함수로 정의할 때
        // 계산량이 많아서 캐시가 필요할 때

        // methods 속성
        // 뷰의 이벤트 핸들러 로직을 함수로 정의할 때
    });
    </script>

</body>
</html>

 

computed :

템플릿 내에 표현식을 넣으면 편하다. 하지만 간단한 연산 일때만 이용하는 것이 좋은데,

왜냐하면 너무 많은 연산을 템플릿 안에서 하면 코드가 비대해지고 유지보수가 어렵다.

 

복잡한 로직이면 사용하는게 computed 속성

 

 

component_basic.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        .fruit_style {
            border : 1px solid #ccc;
            background-color: white;
            padding-left : 1em;
        }
    </style>
</head>
<body>
    <div id="app">
        <h1>{{ sTitle }}</h1>
        <favorite-fruits></favorite-fruits>
        <favorite-fruits></favorite-fruits>
    </div>
    <script>
        Vue.config.devtools=true;
        Vue.component('favorite-fruits',{
            data: function() {
                return {
                    aFruits : 
                [
                    { sFruitName : '사과'}
                    ,{ sFruitName : '오렌지'}
                    ,{ sFruitName : '수박'} 
                ]
                }
            },
            template:`
            <div>
                <div v-for="item in aFruits" class = "fruit_style">
                    <p>좋아하는 과일 : {{ item.sFruitName }}</p>
            </div>
                <br>
            </div> `
        })
    new Vue({
        el : '#app',

        data : {
            sTitle : 'How are you?'
        }
    });
    </script>

</body>
</html>

template :

HTML, CSS 등의 마크업 속성과 뷰 인스턴스에서 정의한 데이터 및 로직들을 연결하여

사용자가 브라우저에서 볼 수 있는 형태의 HTML로 변환해 주는 속성입니다.

 

 

<div>
                <div v-for="item in aFruits" class = "fruit_style">
                    <p>좋아하는 과일 : {{ item.sFruitName }}</p>
                </div>
                <br>

</div> `

 

이 형태를 HTML로 변환해줘서 보여준다.

머스태시를 쓸 수 있다.

 

 

 

 

methods.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        /* 화면에 렌더링 할 때 사용할 제목의 색상을 파란색으로 정의 */
        .blue_style {color: blue}
    </style>
</head>
<body>
    <div id="app">
        <p>클릭 숫자   : {{ nClicks }}</p>
        <p>카운트 다운 : {{ fnCounter }}</p>
        <button v-on:click="fnIncrement"> 눌러줘 </button>
    </div>
    <script>
        Vue.config.devtools=true;
    new Vue({
        el : '#app',

        data : {
            nClicks: 0
        },
        computed: {
            fnCounter: function() {
               return 10 - this.nClicks
               //클릭 할 때마다 차감이 된다.
            }
        },
        methods : {
            fnIncrement() {
                this.nClicks++;
                //methods 렌더링이 일어날때마다 항상 실행
            }
        }

    });
    </script>

</body>
</html>

 

methods:

종속된 값이란 개념없이 렌더링이 일어날 때마다 계속 호출이 된다.

 

그러면 computed와 다른 점은 뭘까

 

computed는 계산해야하는 목표 데이터를 정의하는 '선언형' 프로그래밍 방식

종속 대상의 변경이 일어나기 전까지는 호출이 되지 않고

 

methods는 렌더링이 일어날 때마다 항상 함수를 실행합니다.

 

computed는 반환하는 결과를 캐싱 해줍니다.

 

하지만 헤비, 무거운 로직이라면 렌더링이 일어날 때마다 항상 실행하는 methods보단

computed 속성으로 구현하는게 더 좋다.

 

 

'Front-End > Vue' 카테고리의 다른 글

[Vue] 기초  (0) 2021.07.22
  Comments,     Trackbacks