@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; // 페이지 리스트에서 마지막 페이지 번호
그리고 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를 사용할 수 없다.
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>
디자인 패턴이란 기존 환경 내에서 반복적으로 일어나는 문제들을 어떻게 풀어나갈 것인가에 대한 일종의 솔루션 같은 것입니다. 디자인 패턴 계의 교과서로 불리는 [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 (의존 역전 원칙)
- 의존 관계를 맺을 때, 변화하기 쉬운것 보단 변화하기 어려운 것에 의존해야 한다는 원칙이다. - 변화하기 어려운 것이란 추상적인 것을 말한다. 객체지향적인 관점에서 보자면 변화하기 쉬운것이란 구체화 된 클래스를 의미하고, 변화하기 어려운 것은
추상클래스나 인터페이스를 의미한다
- 의존관계를 맺을 때, 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺는다는 것을 의미한다.
<!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>