1. 태그 사용 시 주의사항 

1) HTML Tag는 중첩 사용 가능

 - 태그 안에 태그를 정의하는 방법(닫힘 순서에 주의). 안쪽 태그부터 닫자!

 

 2)  부모 자식관계의 태그는 다른 태그를 넣지 않음

 - <ol>이나 <ul>은 <li>만 갖는다. (<li>는 <ol>이나 <ul> 안에만 온다.)  

 - <dl>은 <dt>, <dd>만 갖는다.

 - <table>은 <thead>, <tbody>, <tfoot>, <tr>만 갖는다.

 - <tr>은 <th>나 <td>만 갖는다.

>> 다른 속성을 넣고 싶다면 자식 태그 안에서 넣으면 된다. <ol><li><string>..

 

3) 부모태그에 속성을 설정하면 자식 태그에 속성이 상속됨

  자식태그에서 동일 속성을 사용하면 부모에서 전달된 속성은 소멸됨

 

4) 주석 

<!-- 내용 -->

 

 

 

 2. tag 

1) 줄변경

- Web Browser는 엔터로 줄변경을 하지 않음

  • <br> : 한 줄 바꿈 / 줄을 변경하고 싶은 곳에서 <br>을 사용! <br/> 보통 이렇게 사용함
           : 문단 내에서 한 줄을 바꿀 때 사용
  • <p> : 문단을 만들 때 사용  
           <p> 문단 내용 </p>

2) 글 목록

- <h1>~<h6>까지 제공됨

- <h1> 큰 글자 / <h6> 작은 글자

- 자동 줄변경의 기능이 있음

 

3) markup

<b> : 진하게

<strong> : 진하게 / reader기에서 강하게 읽어들일 수 있음

<i> : 이탤릭

<u> : 밑줄 / HTML5에서 지원하지 않음 (웹 브라우저에서는 그려서 보여주기는 함)

<strike> : 취소선 / HTML5에서 지원하지 않음

<mark> : 형광펜 효과 (HTML5에서 새로 지원된 태그)

<big> : 글자 크게 (HTML5에서 지원하지 않음)

<small> : 글자 작게

<sup> : 윗첨자

<sub> : 아랫첨자

 

 

 

 3. 속성이 있는 tag 

- 태그에 사용자가 값을 넣어 다양하게 사용해야 할 때

- 형식)  <태그명 속성 = 값 속성 = 값,,,>

- 값은 3가지 형태로 넣음

  ① "로 묶어서 : 속성 = "값" // 값에 공백 포함 가능. 이걸 제일 많이 씀

  ② '로 묶어서 : 속성 = '값'  // 값에 공백 포함 가능

  ③ 그냥 값으로 : 속성 = 값 // 값에 공백 포함 불가

- 태그 속성을 정의하는 순서는 없음

- 속성은 여러 태그에서 같이 사용하는 공통속성과 특정 태그에서만 사용하는 속성이 제공됨

 

1) 글자 태그

- 글자 크기, 글자 색, 글자모양을 변경하는 태그

- font는 글자만 갖는다. 테이블 등을 font태그가 가질 수 없음

- HTML5에서 지원하지 않음

<font size="크기" color="색깔" face="글꼴">글자</font>

  > 크기 : 1~10 

  > 색 : 영어 (black, red)

           RGB (#000000~#FFFFFF) // 앞에서부터 00:R, 00:G, 00:B

  > 글꼴 : 컴퓨터에 설치된 글꼴 모두 사용가능.

     (단, 클라이언트에 존재하지 않는 글꼴을 사용하면 클라이언트의 브라우저 기본 글꼴로 보여줌. 해결하려면 web font 사용)

 

2) 선긋기 태그

- Web Browser의 넓이 100%가 기본 설정 (자동 줄변경)

- browser마다 다르게 보여짐 (사용자에게 보여지지 않고(? 보여짐..), 소스를 구분하는 용도로 사용함)

 <hr color = "색깔" size="높이"  width ="넓이"/>

   > 넓이 설정방법

① 비율 (숫자%)

 - 브라우저의 몇 %로 할 건지 지정

 - 가변 / 브라우저의 크기에 따라 변경됨

② 숫자

 - pixcel로 지정

 - 고정 / 브라우저의 크기와 상관없이 항상 고정

             

** 속성 중 하나인 size와 color는  font와 선긋기 등의 태그에 공통으로 사용되는 속성임 => 글로벌 속성이라 함

 

3) 이미지 태그

- 이미지를 넣어서 제공할 때 사용

- 이미지는 모든 확장자가 다 가능!

<img src="이미지 경로" width="넓이" height="높이" title="풍선도움말" alt="엑박떴을 때 보여줄 텍스트"
  border="테두리선두께" name=”이름명”/>

  > 이미지 크기변경(Resize)는 되도록 하지 말자. CPU 엄청 소모됨.

  > width나 height 하나만 바꿔도 나머지 하나가 알아서 맞춰서 바뀜

  > 이미지 경로 (!절대경로는 절대 사용하지 말자!) :

  • URL
    - 다른 서버의 이미지도 연결해서 보여줄 수 있음
    - 장점) HTML파일의 위치가 변경되더라도 이미지의 경로를 수정할 필요 없음
    - 단점) 경로가 길다.
    - 사용법) src =http://~/이미지명.확장자" - 제일 많이 씀!!
  • 상대경로
    - 서버에 존재하는 이미지만 연결해서 보여줄 수 있음
    - 장점) 경로를 짧게 표현할 수 있음
    - 단점) HTML 파일의 위치가 변경되면, 이미지의 경로를 수정해야 함
    -  사용법) src ="../이미지명.확장자"
    -  HTML 파일이 존재하는 위치로부터 경로를 연산함

4) 다른 HTML과 연결 (Hyper Text) 

- URL과 상대경로를 사용하여 다른 HTML 페이지와 연결 (Hyper Link)

- 사용법) <a href = "연결할 HTML의 주소" target = "연결된 HTML이 보여질 frame"  name ="이름"  id ="아이디"> 링크명 </a>

    > 연결할 HTML의 주소는 URL or 상대경로로 표현함

    > target을 " 아무거나 "로 주면 새창으로 뜨는데 보통 blank나 new로 줌

    > name 과 id는 글로벌 속성임

    > name속성은 한 페이지에서 특정 위치로 이동할 때 사용함

       : href="#name 속성명" => 링크 클릭 시 name 부분으로 이동함

- 연결된 페이지는 web browser 안의 내용을 모두 삭제하고 다시 그려서 보여줌 (화면 깜빡임이 발생함)

 

5) frame

- 하나의 웹 브라우저에서 여러 개의 HTML을 동시에 보여줄 때 

- frame, iframe (inner frame의 약자) 을 제공

  • frame : 페이지의 전체를 나눌 때 사용 / 페이지의 디자인을 만들 때 사용 / 요새 잘 안씀 / Ex. Java API
  • iframe : 페이지내의 일부분을 나눌 때 사용 / 페이지의 일부분을 변경하여 보여줄 때 사용 / 이걸 더 많이 씀

* iframe 

- 사용법) <iframe name = "이름" src ="최초 제공 페이지의 URL" frameborder="선의 두께" width ="넓이" height="높이" scrolling="스크롤바의 제공여부">  </iframe>

  > name은 태그에서 찾아갈 이름

  > scrolling은 yes/no/auto 중 하나로 설정 가능

- HTML안에 다른 HTML을 넣어서 보여주기 가능

- HTML안의 사진을 눌렀을 때 다른 HTML 보여주기 가능 >> 외부 HTML

 

6) 목록 태그 (list tag)

- ol, ul, dl 3가지의 목록을 제공

  • <ol> 
    - 순서 목록
    -  순서가 있는 데이터를 보여줄 때 
    - 숫자 / 알파벳 / 로마자의 순서를 제공하는 목록 태그
    - <li> : 목록을 보여줄 때 사용. 자동 줄바꿈이 됨
    - 사용법) <ol type = "시작할 값">               
                    <li> 목록 </li> ......        
              </ol>
       > ol에 시작할 값을 주지 않으면 숫자로 시작함    
       > li태그의 value 속성으로 번호를 건너뛸 수 있음. 중간 list 번호를 건너뛰고 싶으면 <li value = "설정할 값">
  • <ul> 
    - 도형 목록
    - 순서없는 목록
    - 사용법) <ul type="도형의 종류">          
                      <li> 데이터 </li> .......        
                </ul>
     
     > 도형 종류 : square, circle, disc // disc가 기본
  • <dl>
    - 설명 목록 - 어떤 대상에 대한 설명을 보여줄 때 
    - <li>를 쓰지 않음

    - 사용법) <dl>              
                      <dt> 제목 </dt>              
                      <dd> 설명 </dd> .....
    // dd는 들여쓰기가 된다.        
                 </dl>

 

7) 테이블 태그

- 표 만들기

 <table>, <tr>,<th>,<td>, <thead>, <tbody>, <tfoot>으로 구성되는 태그

    (_얘네는 필수, 나머진 선택)

      • <tabel>
        - 표를 시작할 때 
        - 높이는 행마다 / 넓이는 열마다 같음
        - 사용법) <table border = "선의 두께" bordercolor="선색" width ="테이블넓이" height ="테이블높이"
                         align ="수평정렬" bgcolor="바닥색" background="바닥에 들어갈 이미지" 
                         cellspacing="td(셀) 간 간격" cellpadding="td 내부 간격(여백)">
        *테이블 자체에 넓이와 높이를 설정하는 것보다 안쪽 td나 th에서 넓이와 높이를 설정하고 테이블에 적용하는 걸 권장함*
      • <tr>
        - 행을 만들 때
        - 안에 존재해야 함
        - 사용법) <tr bgcolor="바닥색" background="바닥에 들어갈 이미지" align="수평정렬" valign ="수직정렬">
        > 수평정렬 : align = "left,center,right"
        > 수직정렬 : valign = "top,middle,bottom"
      • <th>
        - 테이블 header

        - 컬럼의 제목을 만들 때
        - 입력되는 글자는 가운데 정렬, 진하게의 설정이 자동 설정됨
        - 사용법) <th bgcolor="바닥색" background="바닥에 들어갈 이미지" align="수평정렬" valign ="수직정렬"
                         width ="넓이" height ="높이" colspan ="합칠 칸의 수" rowspan="합칠 행의 수">
      • <td>
        - 컬럼의 데이터

        - 입력되는 글자가 왼쪽정렬에 일반 글자로 설정됨
        - <tr>안에 존재해야 함
        - <th>와 속성이 같음
        - 사용법) <td bgcolor="바닥색" background="바닥에 들어갈 이미지" align="수평정렬" valign ="수직정렬"
                         width ="넓이" height ="높이" colspan ="합칠 칸의 수" rowspan="합칠 행의 수">

      • <thead>, <tbody><tfoot>
        -  각각 사용자의 눈에 보이지 않고 "테이블의 제목/내용/결과 부분"임을 알려줄 때

 

'Development > HTML CSS' 카테고리의 다른 글

[HTML] WEB/ HTML  (0) 2021.10.23

 Web 

- World Wide Web / Web / W3

- 인터넷에서 문자, 그림, 미디어(소리, 영상)를 포함하는 문서(HTML 문서)를 HyperText 개념을 사용하여 검색하고 전송할 수 있는 서비스

- HTML을 요청하고 해석할 수 있는 프로그램과 (=Web browser) 요청한 HTML을 응답해줄 수 있는 프로그램 (=Web Server)으로 구성됨

 

* Web Server

- HTML을 저장하고 있다가 인터넷에서 HTML이 요청되면 HTML을 응답해주는 프로그램 (Apache HTTP Server)

 

* Web Container

- Web Server의 기능을 가지고 있음 (HTML 파일 응답 가능)

- JSP, Servlet을 저장하고 있다가 Web Client에서 요청이 오면 JSP나 Servlet을 HTML로 변환하여 응답해주는 프로그램

   (Apache Tomcat)

 

* Web Client

- Web browser를 사용하여 Web Server로 요청을 보내고, 응답받은 HTML을 Rendering(그려주는)하여 보여주는 프로그램

 

 

 

 HTML (Hyper Text Markup Language) 

- 1995년 Tim, berners Lee가 HTML 1.0 초안을 발표

- 마크업(Markup Language) 언어 : 본문에서 특정부분을 강조하여 보여줄 수 있는 언어

- Web에서 HTML을 주고받기 위한 통신 규약 : HTTP (Hyper Text Transmission Protocol)

- Tag 언어 : <태그명> 내용 </태그명>의 형식으로 제작되는 언어

- SGML에서 필요한 것만 정의하여 만든 언어

- HTML은 연산의 기능이 없으며, 컴파일 하지 않고 Web browser에서 Rendering 하는 언어

  -> 그냥 그리기만 하는 언어다!

 

* HTML 작성법

- DTD(Document Type Definition)가 정의된 마크업 언어

     > Tag가 미리 정의되어 있음

     > 사용할 HTML 태그를 정의한 문서

- HTML 문서 작성 시 DTD에 정의된 것만 사용할 수 있음 

 

* 작성시 주의

- 대소문자를 가리지 않음 (하지만 맞춰 써야 함!)

- 태그는 짝으로 작성함

- ‘<’와 태그명 사이에는 공백을 넣지 않음

   Ex) <ol   > : 인식 가능 / <    ol> : 인식 불가능

 

* 태그 구성

- <시작태그> 내용 </끝태그>  이 전체를 element 라고 함 (태그=element)

  <열림태그> 내용 </닫힘태그>

'Development > HTML CSS' 카테고리의 다른 글

[HTML] tag  (0) 2021.10.24

 

 Procedure 호출 

- Procedure : 쿼리문을 저장하고 필요한 곳에서 실행해야 할 때 사용하는 DBMS 객체

- DBMS에서 언어적인 요소(제어, 연산)를 가져야 할 때

- Oracle DBMS에서는 user_procedures 딕셔너리에서 생성된 Procedure를 확인할 수 있음

- CallableStatement 객체로 Procedure를 호출할 수 있음

- 자바에서 호출 시, SQL의 프로시저가 컴파일 되어있어야 함

 

* Oracle의 Procedure *

- PL/SQL : DBMS에서 언어적인 요소를 구현할 때 사용

- 기본문법(데이터형, 변수, 연산자, 제어문), cursor, function, procedure, trigger, package를 지원

- 컴파일을 한 후 실행함

    > 컴파일 : @파일명.sql

    > 함수 : 간접실행 (쿼리문에 함수를 포함시켜 실행하는 방법)

                   직접실행 (실행기를 사용하여 실행하는 방법 -> execute 또는 exec 사용)

- 작성법) 

    create or replace procedure 프로시저명 (매개변수,,,) 

    is  -- 변수 선언, record 선언(java의 VO같은 거), table 선언(java의 배열 같은 거), cursor 선언

    begin -- 코드 작성(연산, 제어, 쿼리작성 등등)

    end;

    /

- 선언 시 주의)

   > 매개변수 종류 : in parameter (프로시저 외부의 값을 프로시저 내부로 전달하는 일)  //  in은 생략하고 선언 가능

                                   our parameter (프로시저 안에서 처리한 값을 외부로 전달하는 일)

   > 매개변수명은 테이블의 컬럼명과 동일하면 절대 안됨! 식별될 수 있도록 선언 

        ex) sal로 쓰면 sal=sal 항상 참이 될 수 있으므로 in paramter면 in_sal로 선언

- 컴파일) @파일명.sql   -- DOS에서 sqlplus에서 로그인하고 하면 됨

- 실행)

   1) bind 변수 선언 (프로시저 내부의 처리된 값을 SQLPlus에서 저장할 변수)

        > 프로시저 내부의 처리된 값은 out parameter를 타고 나옴

        >  bind 변수는 일시적이라 실행될 때 마다 선언해주어야 함

             var | variable 데이터형 변수명 (크기) -- 크기는 넣어도 되고 안 넣어도 됨

    2) 프로시저를 실행 (직접실행)

        execute | exec 프로시저명 (,,, : 바인드변수명 ,,,) 

        > : in parameter로 들어가는 값 

        > : 바인드면수명 : out parameter로 나오는 값

    3) bind 변수에 저장된 값 출력 (procedure가 처리한 결과값)

        > print 바인드변수명 바인드변수명 바인드변수명 -- 띄어쓰기로 여러 개의 값 줄 수 있음

 

-프로시저 삭제) drop procedure 프로시저명;

 

 

 Java에서의 Procedure 호출 

* CallabeStatement

- procedure를 호출할 때 사용하는 객체

- PreparedStatement Interface의 하위 Interface

- PreparedStatement bind 변수를 사용하여 값을 입력할 수 있음

- SQLPlus의 bind 변수는 CallableStatement의 registerOutparameter로 처리

   > SQLPlus의 bind 변수 : var 변수명 데이터형(크기), procedure에서는 :변수명 으로 사용

 

- 사용법)

1) 쿼리문 생성객체 얻기

    CallabeStatement cstmt = con.prepareCall(프로시저명);

    // 프로시저명에는 이렇게 !  "{ call 프로시저명(?,?,…) }"

2) CallableStatement 바인드 변수에 값 설정

 ① in parameter에 대응되는 bind 변수 (프로시저에 값 할당)

      - cstmt.setXXX (인덱스, 값);

         number -> cstmt.setInt (인덱스, 값), cstmt.setDouble (인덱스, 값);

         varchar2, char -> cstmt.setString(인덱스, 값);

 ② out parameter에 대응되는 bind 변수 (SQLPlus의 bind 변수를 자바 형식으로 선언하는 것)

      - cstmt.registerOutParameter (인덱스, java.sql.Types.상수) ;

      - Oracle에서는 var 변수명 데이터형(크기) 이렇게 했던 걸 위처럼 처리

    * java.sql.Types 클래스 

       - DBMS의 bind변수를 정의할 때 사용하는 데이터형을 java에서 사용할 수 있도록 구현한 클래스 

       - 특정 DBMS에서만 제공하는 데이터형을 지원하지 않음

            > Oracle의 NUMBER 대신 Types.NUMERIC 사용 

            > Oracle의 VARCHAR2 -> Types.VARCHAR

3) 프로시저 실행 

 - CallableStatement는 실행 method가 없으므로, 부모 Interface의 실행 method를 사용함

     > executeQuery(), executeUpdate(), execute() 셋 중에서 실행했다는 의미로 excute()를 사용!

        (어차피 셋다 반환형(rs,int,boolean)이 제대로 나오지 않음)

 * 프로시저를 실행하면 바인드변수(registerOutParameter)에 프로시저가 실행된 결과가 저장됨

4) 실행결과가 저장된 값 받기

- ResultSet 안 씀.

- CallableStatement의 method 사용 : cstmt.getXXX (바인드변수의 인덱스)

 예) registerOutParameter(Types.NUMERIC) 일 때 : cstmt.getInt(인덱스); cstmt.getDouble(인덱스)

        registerOutParameter(Types.VARCHAR) 일 때 : cstmt.getString(인덱스);

 


# 오늘의 코딩 #

- Java에서 Oracle에 생성한 procedure를 사용해보자 

 

# 생성한 프로시저

- in parameter로 name을 받고, out parameter로 msg를 내보냄!

 

# Java에서 호출

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;

import javax.swing.JOptionPane;

import test.dao.DbConnection;

/**
 * 이름(varchar2)을 입력하여 (in parameter) 설정된 msg(varcahr2)를 받는 일 (out parameter)
 * @author user
 */
 
public class UseCallableStatement {

	public UseCallableStatement() throws SQLException {

		Connection con = null;
		CallableStatement cstmt = null;

		DbConnection db = DbConnection.getInstance(); // 중복된 일이라 method 만들어 놓음
		
		try {
			// 1.드라이버로딩& 2.커넥션얻기
			con = db.getConn();
			// 3.쿼리문 생성객체 얻기 (프로시저명은 proc_hello)
			cstmt = con.prepareCall("{call proc_hello (?,?)}");
			// 4.바인드 변수 설정
			// in parameter
			cstmt.setString(1, JOptionPane.showInputDialog("이름 입력"));
			// out parameter
			cstmt.registerOutParameter(2, Types.VARCHAR);
			// 5.쿼리수행 후 결과 얻기
			cstmt.execute();
			//프로시저가 실행된 결과는 Out Parameter에 저장됨
			String msg = cstmt.getString(2);
			System.out.println(msg);
			
		} finally {
			// 6.연결 끊기 : CallableStatement는 PreparedStatement의 자식이므로, 종료 method를 부모로 사용 가능
			db.closeDB(null, cstmt, con);
		} // end finally

	}// UseCallableStatement

	public static void main(String[] args) {

		try {
			new UseCallableStatement();
		} catch (SQLException e) {
			e.printStackTrace();
		} // end catch
	}// main

}// class

- name은 다이얼로그를 띄워서 입력받음 (동동)

 

# 출력 결과 #

-> 완료!! ㅎㅎ 

 

 

'Development > JDBC' 카테고리의 다른 글

[JDBC] Cursor 작성 예정  (0) 2021.10.21
[JDBC] ResultSetMetaData / Transaction 처리  (0) 2021.10.19
[JDBC] DAO / PreparedStatement 활용  (0) 2021.10.15
[Java/JDBC] Singleton Pattern  (0) 2021.10.14
[JDBC] SQL Exception / SQL Injection  (0) 2021.10.13

 1. ResultSetMetaData 

- data dictionary를 사용하지 않고, 실행되는 select 쿼리에 해당하는 컬럼정보를 얻을 때 사용하는 ineterface

- 조회된 inline view에 한하여 컬럼정보 조회 가능

- DBMS가 달라도 정보를 얻을 수 있음

   > Oracle의 user_cons_columns ,,, 등과 같은 다양한 data dictionary는 oracle DB에서만 존재/사용 가능

- ResultSet을 기반으로 생성되며, ResultSet은 연결 끊어야 하나 ResultSetMetadata는 연결 안 끊어도 됨

- 사용법) 

> ResultSet에서 ResultSetMetaData를 얻기 : ResultSetMetaData rsmd = getMetaData();

> 정보 얻기

  • 컬럼의 개수 : rsmd.getColumnCount()
  • 컬럼명 : rsmd.getColumnName(컬럼 인덱스) or getColumnLabel (oracle의 컬럼 index는 1부터 시작)
  • 컬럼 데이터형명 : rsmd.getColumnTypeName(컬럼의 인덱스)
  • 컬럼 데이터형크기 : rsmd.getPrecision(컬럼의 인덱스)
  • null 허용 : rsmd.isnullable(컬럼의 인덱스) // not null이면 0 null 허용이면 1

 

 

 2. Transaction 처리 

- 데이터베이스의 작업 단위 (insert, update, delete)

- 작업이 완료될 때 commit, 작업을 취소할 때 rollback 사용 

- java에서는 Connection Interface가 Transaction을 처리

    > Transaction 대상쿼리문 하나 당 작업완료 (auto commit)

- auto commit 문제 : 

    > Transaction 대상 쿼리문 하나로 작업이 완료되면 auto commit은 문제를 발생시키지 않지만,

       여러 개의 쿼리문이 하나의 Transaction을 구성하면 문제를 발생시킬 수 있음

    > 여러 개의 작업이 모두 성공했을 때에만 DB작업이 완료되어야 하고,

       여러 개의 작업 중 하나라도 실패하면 모든 DB작업이 취소되어야 함 

    > 여러 쿼리가 있을 때 Transaction으로 관리하자

- 작업방법)

1) Connection의 auto commit을 해제

     con.setAutoCommit(false) ;  // 개발자가 transaction 완료 후 commit 또는 rollback 해야 함

    *주의 : method 안에서 Connection을 종료하면 commit이 된 후 Connection이 종료되므로,

                 연결 끊기는 DB작업 method 밖에서 끊음 

     (Connection을 DB작업 method 외부에서 선언 <- method가 2개로 분리됨)

2) 쿼리문 작업 외부에 Connection 선언 

3) DB작업 method가 쿼리 수행 행수를 반환

4) DB작업 method가 개발자가 목표로 한 레코드 수를 반환했을 때에만 commit 또는 rollback을 수행하고 연결을 끊음

 

- rollback 처리시 차이점:

1) update/delete의 경우 : 쿼리문 수행 실패 시 0건 수행이므로 else 부분에서 rollback 처리

2) insert의 경우 : 쿼리문 수행 실패 시 SQLException 발생하므로 catch 부분에서 rollback 처리

 

 


# 오늘의 코딩 #

- Transaction을 처리해보자

- 여러 쿼리문을 실행하고 모두 수행되었을 경우에만 commit!

 

# DB 구성은 아래와 같다!

- TRANSACTION2 테이블의 주소컬럼의 크기를 작게 설정하여 INSERT 되지 않을 경우/될 경우를 TEST 예정

TRANSACTION1
TRANSACTION2

 

# Transaction Test할 클래스

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import test.dao.DbConnection;

/**
 * 쿼리문 여러개로 하나의 Transaction이 구성되는 경우의 처리
 * @author user
 */
 
public class TestTransaction {

	/**
	 * transaction을 처리할 쿼리문을 실행하고, 결과를 받아와서 transaction을 완료하는 method
	 */
	@SuppressWarnings("resource")
	public void transaction() {
		Connection con = null;
		try {
			// 2. 커넥션 얻기
			con = DbConnection.getInstance().getConn();
			// autocommit 해제
			con.setAutoCommit(false);
			// 3. 쿼리문 수행 후 결과 얻기
			int cnt = sqlJob(con); // 여러개의 쿼리문이 하나의 transaction을 구성
			// 쿼리문이 실행된 후 목표로 한 행수가 나오면 commit 아니면 rollback
			if (cnt == 2) {
				con.commit(); }// 트랜잭션 완료
		} catch (SQLException e) {
			try {
				 // insert로 트랜잭션이 구성되는 경우 catch 에서 rollback을 처리함
				con.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}//end catch
			e.printStackTrace();
		} finally {
			try {
				if (con != null) {
					con.close();
				} // end if
			} catch (SQLException e) {
				e.printStackTrace();
			} // end catch
		} // end finally
	}// transaction

	/**
	 * Connection을 받아, DB작업을 수행하는 method<br>
	 * transaction1 테이블과 transaction2 테이블에 이름과 주소를 입력하여 추가된 행수를 반환하는 일
	 * 
	 * @param con
	 * @return 쿼리문을 수행한 결과 횟수 (총합)
	 * @throws SQLException
	 */
	public int sqlJob(Connection con) throws SQLException {
		int allCnt = 0;  // 쿼리문 수행 성공 횟수를 담을 변수

		String name = "김동동";
		String addr = "서울시 동작구";

		// 쿼리문 수행객체 얻기
		// transaction1 테이블에 작업
		String insert1 = "insert into transaction1(name,addr) values (?,?)";
		PreparedStatement pstmt = con.prepareStatement(insert1);
		pstmt.setString(1, name);
		pstmt.setString(2, addr);

		int cnt1 = pstmt.executeUpdate();

		// transaction2 테이블에 작업
		String insert2 = "insert into transaction2(name,addr) values (?,?)";
		PreparedStatement pstmt2 = con.prepareStatement(insert2);
		pstmt2.setString(1, name);
		pstmt2.setString(2, addr);

		int cnt2 = pstmt2.executeUpdate();

		allCnt = cnt1 + cnt2;

		return allCnt;
	}// sqlJob

	public static void main(String[] args) {
		TestTransaction tt = new TestTransaction();
		tt.transaction();
	}// main

}// class

 

# 출력 결과 #

1) INSERT 정상 처리 (이름 : 김동동 / 주소 : 서울)

- 두 테이블 모두 정상 적으로 INSERT 처리되어 커밋 됨!

2) 오류 발생 시 에러 메시지(이름 : 김동동 / 주소 : 서울시 동작구)

- 모든 트랜잭션이 처리되지 않으면 커밋되지 않고 에러 발생!

 

 

 

 DAO 

- 실질적으로 DB에 접근하는 객체

- DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트 (보통 singleton 패턴으로 만듦)

 

 


# 오늘의 코딩 #

- 이번엔 SQL Injection이 발생하지 않는 PreparedStatement 활용해보자

- Singleton으로 만들어서 하나의 객체만 생성되고 그 객체가 DB와 연동, Login을 수행해보자

 

# Login VO 클래스

- Login 사용 클래스에서 설정한 ID와 PW를 저장하고 반환해줄 클래스

public class LoginVO {

	private String id, pass;

	public LoginVO() {
		super();
	}

	public LoginVO(String id, String pass) {
		this.id = id;
		this.pass = pass;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPass() {
		return pass;
	}

	public void setPass(String pass) {
		this.pass = pass;
	}

	@Override
	public String toString() {
		return "LoginVO [id=" + id + ", pass=" + pass + "]";
	}
	
}

 

# Login DAO 클래스

- DB와 연결되어 일을 할 클래스

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

import test.dao.DbConnection; 

public class LoginDAO {
	
	public String usePreparedStatement(LoginVO loginVO) throws SQLException {
		String name =""; 
		
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		DbConnection db = DbConnection.getInstance();
		try {
		//1.드라이버로딩 & 2.Connection얻기 (중복된 일이라 따로 method 만들어놨음)
			con = db.getConn();
		//3.쿼리문 생성 객체 얻기
			StringBuilder selectName = new StringBuilder();
			selectName
			.append("	select	name ")
			.append("	from 	test_login	")
			.append("	where id = ? and pass = ? ");  // ? : 바인드 변수!!
			
			pstmt = con.prepareStatement(selectName.toString());
		//4.바인드 변수에 값 설정 (VO Class에서 가져오기)
			pstmt.setString(1, loginVO.getId());
			pstmt.setString(2, loginVO.getPass());
		//5.쿼리문 수행 후 결과 얻기
			rs = pstmt.executeQuery();
			
			if(rs.next()) {//입력된 id와 p/w에 일치하는 레코드가 존재
				name = rs.getString("name"); // 조회된 이름 가져와서 변수에 할당
			}//end if
			
		}finally{
		//6.연결 끊기 (얘도 method 만들어놨음)
			db.closeDB(rs, pstmt, con);
		}//end finally
		
		return name;
	}//usePreparedStatement
	
}//class

 

# Login을 수행할 클래스

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.sql.SQLException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

@SuppressWarnings("serial")
public class TestLogin extends JFrame implements ActionListener {

	private JTextField jtfId;
	private JPasswordField jpfPass;
	private JLabel jlblOutput;
	
	public TestLogin() { 
		super("로그인");
		jtfId=new JTextField();
		jpfPass=new JPasswordField();
		jlblOutput=new JLabel("출력");
		
		jtfId.setBorder(new TitledBorder("아이디"));
		jpfPass.setBorder(new TitledBorder("비밀번호"));
		jlblOutput.setBorder(new TitledBorder("결과"));
		
		jpfPass.addActionListener(this);
		
		setLayout(new GridLayout(3,1));
		
		add(jtfId);
		add(jpfPass);
		add(jlblOutput);
	
		// 종료
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				dispose();
			}//windowClosing

			@Override
			public void windowClosed(WindowEvent e) {
				System.exit(JFrame.ABORT);
			}//windowClosed
		});
		setBounds(100, 100, 250, 300);
		setVisible(true);
	}//TestLogin
	
	public void usePreparedStatement() {
		LoginDAO ld = new LoginDAO();
		
		//아이디와 비밀번호를 받자
		LoginVO lv = new LoginVO(jtfId.getText(), new String(jpfPass.getPassword()));
		
		try {
			String name = ld.usePreparedStatement(lv);
			
			if("".equals(name)) {//이름이 조회되지 않음
				JOptionPane.showMessageDialog(this, "아이디나 비밀번호를 확인해주세요.");
				return;
			}//end if
			
			jlblOutput.setText(name + "님 반갑습니다.");
			
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(this, "죄송합니다.");
			e.printStackTrace();
		}//end catch
		
	}//usePreparedStatement
	
	@Override
	public void actionPerformed(ActionEvent e) {
		usePreparedStatement(); // SQLInjection 공격불가
	}//actionPerformed

	public static void main(String[] args) {
		new TestLogin();
	}//main

}//class

 

# 출력 결과 #

(기존 DB 데이터의 ID와 비밀번호는 각각 WOO/4321)

 

> 로그인 성공시 화면

 

> 로그인 실패시 화면

 

 Singleton Pattern 

- JVM에서 하나의 인스턴스를 생성하고 사용하는 개발 패턴 (Design pattern)

- 메모리를 절감할 수 있음

- 참조하는 속도가 빠름

- Framework에서 주로 객체를 생성하고 사용하는 방식

- 아래 작성법이 class안에 들어가있으면 singleton 패턴인 클래스 라고 함

- Ex) Calendar Class의 getInstance()

- 작성법)

작성 코드
1. 객체화를 클래스 안에서만 할 수 있도록 작성함
 => 생성자의 접근지정자를 private로 지정

2.  객체를 생성하여 반환하는 일을 하는 method 작성
 => public static 클래스명 getInstance() {

3. 객체를 하나로 유지하고 반환하는 일.
      if ( 객체 == null ) { 객체 = new 생성자();  }
                return 객체;
    }
class Test{
        private static Test t;
        private Test(){
         }
       
        public static Test getInstance(){
                 if (t == null) {
                     t = new Test();  }
                 return t;       }    
        }

 


 

# 예시 #

- 싱글톤 패턴의 클래스

/**
 * 싱글톤 패턴으로 만드는 클래스 : 객체가 하나로만 사용되는 클래스
 * @author user
 */
public class Singleton {

	private static Singleton single;
	private Singleton() { // 접근지정자 private으로 클래스 외부에서 객체화 불가
		
	}// Singleton

	/**
	 * 생성된 객체를 얻기 위한 method
	 * @return
	 */
	public static Singleton getInstance() {
		// 객체를 하나로 생성 관리할 수 있는 코드
		if (single == null) {
			single = new Singleton();
		}//end if
		
		return single;
	}// getInstance

}// class

 

- 싱글톤 클래스 사용

public class UseSingleton {

	public static void main(String[] args) {
		//생성자가 private이기 때문에 클래스 외부에서 객체화 될 수 없음
//		Singleton single = new Singleton(); // 생성자가 보이지 않아서 생성 불가! getInstance 사용해야함
		
		//getInstance method를 통해서만 객체를얻음
		Singleton single = Singleton.getInstance();
		Singleton single2 = Singleton.getInstance();
		Singleton single3 = Singleton.getInstance();
		
		//몇개를 생성하든 객체는 하나이므로 주소는 똑같당
		System.out.println(single);
		System.out.println(single2);
		System.out.println(single3);
		
		//일반 class는 객체 생성 횟수만큼 heap 주소가 할당됨!
		UseStatement us = new UseStatement();
		UseStatement us2 = new UseStatement();
		UseStatement us3 = new UseStatement();
		
		System.out.println(us);
		System.out.println(us2);
		System.out.println(us3);
		
	}//main

}//class

 

# 출력 결과 #

 

> 객체를 하나만 생성해서 사용할 때 !! 

DB 연동 작업 시 한 객체만 DB에 접근/조작할 수 있도록 할 수 있음

 1. SQLException 

- Connection Class에서 쿼리문 객체를 생성하면 SQLException 발생

- 클래스에서 쿼리문 실행 method 작성 시 연결을 반드시 끊기 위해 try~finally로 작성

     Ex)  Connection con = null; Statement stmt = null;

             try { connection 연결 ~ 쿼리문 생성 객체 얻기~ 쿼리문 수행 후 결과얻기~ }

             finally { conncetion 및 쿼리문 객체 연결 끊기}

-  SQLException은 getErrorCode() 존재 : DB에서 발생한 error code를 확인할 수 있음

     > 반환형 int  

        1 : PK 제약

        1438 : 정수 컬럼의 설정된 길이보다 큰 값이 입력된 경우

        12899 : 문자열 컬럼의 설정된 길이보다 큰 값이 입력된 경우

 

 

 2.SQLInjection 

- Web에서 DBMS의 정보를 탈취하기 위해 가장 많이 발생하는 공격

- 프로그램 외부에서 쿼리문을 예상하여, 나머지 쿼리문을 작성하여 입력하는 방법

- 방어 방법) PreparedStatement를 사용 / injection block code 작성

- Statement 사용시 발생 / PreparedStatement 사용하면 발생 X

   Ex) login창에서 id와 password를 입력받아서 인증을 진행하는 경우

   > Statement 사용시, JTextArea로 사용자로부터 값을 입력받을 때 ' or 1=1 -- 로그인 창에서 id에 id대신 이것을 입력하면

       항상 참이 되어 첫번째 사람 정보로 로그인 됨

   > SQLInjection 발생

   > 따라서, Injection 방어 코드를 아래와 같이 만들어서 쿼리 실행문에 적용해야 함

- PreparedStatement를 사용하면 방어 코드 필요 없음!!

 

 

# 방어코드 작성 예시 #

	/**
	 * SQLInjection 방어하기<br>
	 * 입려값에 공백, 주석, ', 쿼리문이 입력되면 해당 데이터를 삭제함
	 * @return
	 */
	public String blockInjection(String value) {
		//' or 1=1 --  : 로그인 창에서 id에 id대신 이것을 입력하면 항상 참이 되어 첫번째 사람 정보로 로그인 됨 
		//>> SQLInjection 발생!
		return value.replaceAll(" ", "").replaceAll("--", "").replaceAll("'", ""); 
        // --대신 -해도 됨
		
	}//blockInjection

 

# 방어코드 적용 예시 #

 // 쿼리문 생성 객체 작성시
			StringBuilder selectName = new StringBuilder();
			selectName
			.append("	select	name ")
			.append("	from 	test_login	")
			.append("	where	id='").append(blockInjection(loginVO.getId())).append("' and pass='")
			.append(loginVO.getPass()).append("'");

 

이렇게 적용하면 Statement 도 SQL Injection 방어 가능 !_!

+ Recent posts