해당 테스트는 Cent OS7의 open-jdk 1.8을 기준으로 진행했습니다.

# which java
/usr/bin/java

# ll /usr/bin/java
lrwxrwxrwx. 1 root root 22 Apr 25 22:19 /usr/bin/java -> /etc/alternatives/java

# ll /etc/alternatives/java
lrwxrwxrwx. 1 root root 73 Apr 25 22:19 /etc/alternatives/java -> /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64/jre/bin/java

# cd /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64/
# ls
ASSEMBLY_EXCEPTION  bin  include  jre  lib  LICENSE  tapset  THIRD_PARTY_README
 

1. AES 란?

AES는 고급 암호화 표준이라는 의미이며, 암호화 및 복호화 시 동일한 키를 사용하는 대칭키 알고리즘입니다.

AES의 종류는 AES-128, AES-192, AES-256이 있고 각각 뒤에 붙은 숫자가 키의 길이를 의미합니다.

AES 암호화 알고리즘은 높은 안정성과 빠른 속도로 전세계적으로 사용되고 있습니다.

 

2. AES 암호화 설명

1) Secret Key

Secret Key는 평문을 암호화하는데 사용되며 절때로 외부에 노출되어서는 안됩니다.
AES의 종류가 무엇이냐에 따라 Secret Key의 길이가 달라집니다.

(AES-256는 256비트(32바이트)의 키를 사용합니다.)

 

2) Block Cipher

AES는 128비트(16바이트)의 고정된 블록 단위로 암호화를 수행합니다. (이는 암호화 키의 길이와 전혀 무관합니다)
암호화를 수행할 때 여러가지 Block Cipher Mode를 선택할 수있으며 크게 CBC, ECB 등이 있습니다. 
권장하는 방식은 CBC 방식입니다.

AES는 128비트의 블록단위로 암호화를 수행하는데 128비트보다 작은 블록이 생길 경우 부족한 부분을 특정 값으로 채워야합니다.
이러한 작업을 패딩이라고 부르며, 대표적으로 PKCS5, PCKS7 방식이 있습니다.

 

3) CBC (Ciper Block Chaning)

https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

AES는 128비트의 고정된 블록 단위로 암호화를 수행하는데, CBC는 블록을 그대로 암호화 하지않고 이전에 암호화했던 블록과 XOR 연산을 한 다음에 암호화를 수행합니다.

그래서 같은 내용을 갖는 원문 블록이라도 전혀다른 암호문을 갖게됩니다. 그런데 첫번째 블록은 이전 암호화 블록이 없기 때문에 이를 위해 IV(initialization vector)를 이용합니다.

AES는 128비트(16바이트)단위로 암호화 하기때문에 IV또한 16바이트 크기여야합니다. IV가 생성되면 이 값을 가지고 첫번째 블록을 암호화 합니다. 매번 다른 IV를 생성하면 같은 평문이라도 다른 암호문을 생성할 수 있습니다.

 

3. AES-256 예제

public class Main {

    public static void main(String[] args) throws Exception {

        AES256 aes256 = new AES256();
        String text = "!! Hello World !!";
        String cipherText = aes256.encrypt(text);
        System.out.println(text);
        System.out.println(cipherText);
        System.out.println(aes256.decrypt(cipherText));

    }

}

 

참조
https://perfectacle.github.io/2019/11/24/aes/
http://happinessoncode.com/2019/04/06/java-cipher-algorithm-mode-padding/

1. SHA-256란?

SHA-256은 SHA(Secure Hash Algorithm) 알고리즘의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환합니다.
해시 알고리즘 SHA-2 계열 중 하나이며, 2^256만큼 경우의 수를 만들 수 있습니다.

SHA-256 해시 함수는 어떤 길이의 값을 입력하더라도 256비트의 고정된 결과값을 반환합니다.
입력 값이 조금만 변동되더라도 반환값이 완전히 달라지기 때문에 반환값을 토대로 입력값을 유추하는 것이 거의 불가능하다고 합니다.

SHA-256은 단방향 암호화 방식이기 때문에 복호화가 불가능합니다.
복호화를 하지 않아도 되기 때문에 속도가 빠르며, 사용 예로 비밀번호의 일치여부 확인이 있습니다.

여기서 단방향 암호화란 평문을 암호화 했을 때, 원래 평문으로 복호화할 수 없는 암호화를 의미합니다.

 

2. SHA-256 예제

JAVA에서 SHA 암호화를 사용하려면 java.security.MessageDigest 클래스를 사용해야합니다.

import java.security.NoSuchAlgorithmException;

public class Main {
    
    public static void main(String[] args) throws NoSuchAlgorithmException {
        SHA256 sha256 = new SHA256();
        
        //비밀번호
        String password = "hi12345678";
        //SHA256으로 암호화된 비밀번호
        String cryptogram = sha256.encrypt(password);
        
        System.out.println(cryptogram);
        
        //비밀번호 일치 여부
        System.out.println(cryptogram.equals(sha256.encrypt(password)));
        
    }
    
}

/*
  결과 값
  3d939b8a32d9e0138935522f8c524b3fbe2d5cc39bf6a2c04805f890b11f3bdb
  true
*/

 

참조
http://wiki.hash.kr/index.php/SHA256#cite_note-8

apache poi 라이브러리를 이용해서 자바, 또는 웹상에서 Excel 파일 다운로드 기능을 만들어 보겠습니다.

1. poi란?

아파치 POI(Apache POI)는 아파치 소프트웨어 재단에서 만든 라이브러리로서 마이크로소프트 오피스 파일 포맷을 순수 자바 언어로서 읽고 쓰는 기능을 제공한다.

출처 : https://ko.wikipedia.org/wiki/%EC%95%84%ED%8C%8C%EC%B9%98_POI

 

2. poi 사용 예제

1) poi library 추가 (maven)

        <!-- xls 전용 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.0.0</version>
        </dependency>

        <!-- xlsx 전용 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.0.0</version>
        </dependency>
용도에 맞게 pom.xml에 해당 라이브러리를 추가해주세요.

 

2) ExcelUtil (File, HttpServletResponse)

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

public class ExcelUtil {

    private int rowNum = 0;
    
    //File로 만들 경우
    public void createExcelToFile(List<Map<String, Object>> datas, String filepath) throws FileNotFoundException, IOException {
        //workbook = new HSSFWorkbook(); // 엑셀 97 ~ 2003
        //workbook = new XSSFWorkbook(); // 엑셀 2007 버전 이상

        Workbook workbook = new SXSSFWorkbook(); // 성능 개선 버전
        Sheet sheet = workbook.createSheet("데이터");

        rowNum = 0;

        createExcel(sheet, datas);

        FileOutputStream fos = new FileOutputStream(new File(filepath));
        workbook.write(fos);
        workbook.close();

    }
    
    //HttpServletResponse 경우
    public void createExcelToResponse(List<Map<String, Object>> datas, String filename, HttpServletResponse response) throws IOException {
        Workbook workbook = new SXSSFWorkbook(); // 성능 개선 버전
        Sheet sheet = workbook.createSheet("데이터");

        rowNum = 0;

        createExcel(sheet, datas);
        
        // 컨텐츠 타입과 파일명 지정
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", String.format("attachment;filename=%s.xlsx", filename));
        
        workbook.write(response.getOutputStream());
        workbook.close();
    }

    //엑셀 생성
    private void createExcel(Sheet sheet, List<Map<String, Object>> datas) {
    
        //데이터를 한개씩 조회해서 한개의 행으로 만든다.
        for (Map<String, Object> data : datas) {
            //row 생성
            Row row = sheet.createRow(rowNum++);
            int cellNum = 0;
            
            //map에 있는 데이터를 한개씩 조회해서 열을 생성한다.
            for (String key : data.keySet()) {
                //cell 생성
                Cell cell = row.createCell(cellNum++);
               	
                //cell에 데이터 삽입
                cell.setCellValue(data.get(key).toString());
            }
        }
    }

}

 

3) File 생성인 경우

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {

    public static void main(String[] args) throws FileNotFoundException, IOException {

        List<Map<String, Object>> datas = new ArrayList<>();

        Map<String, Object> data1 = new HashMap<>();
        data1.put("id", 1);
        data1.put("name", "kim");

        Map<String, Object> data2 = new HashMap<>();
        data2.put("id", 2);
        data2.put("name", "park");
        
        datas.add(data1);
        datas.add(data2);

        ExcelUtil excelUtil = new ExcelUtil3();

        String filepath = "D:/data.xlsx";
        excelUtil.createExcelToFile(datas, filepath);
    }

}

 

4) HttpServletResponse에 write 할 경우

    @GetMapping(value = "/excelDownload")
    public void excelDownload(
            HttpServletResponse response
    ) {
        List<Map<String, Object>> datas = new ArrayList<>();

        Map<String, Object> data1 = new HashMap<>();
        data1.put("id", 1);
        data1.put("name", "kim");

        Map<String, Object> data2 = new HashMap<>();
        data2.put("id", 2);
        data2.put("name", "park");

        datas.add(data1);
        datas.add(data2);

        ExcelUtil excelUtil = new ExcelUtil();

        excelUtil.createExcelToResponse(
                datas,
                String.format("%s-%s", "data", LocalDate.now().toString()),
                response
        );
    }
05-Apr-2021 15:38:25.517 INFO [java-sdk-http-connection-reaper] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
 java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
        at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1372)
        at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1360)
        at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1219)
        at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1180)
        at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
        at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
        at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
        at ch.qos.logback.classic.Logger.log(Logger.java:765)
        at org.apache.commons.logging.LogAdapter$Slf4jLocationAwareLog.debug(LogAdapter.java:470)
        at com.amazonaws.http.IdleConnectionReaper.run(IdleConnectionReaper.java:190)
위와 같은 오류가 발생 할 경우 tomcat destroy 시 logback을 종료시키는 코드를 넣어주어야 합니다.
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;

public class ShutdownHookConfiguration {

    public void destroy() {

        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        loggerContext.stop();

    }
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean(destroyMethod = "destroy")
    public ShutdownHookConfiguration shutdownHookConfiguration() {
        return new ShutdownHookConfiguration();
    }

}

 

spring boot를 사용하다보면 명시적으로 자원을 close 해주어야 하는 경우가 있습니다.
이때 spring boot destory event 발생 시 자원을 close해주는 코드를 추가해주면 됩니다.
public class ShutdownHookConfiguration {

    public void destroy() {
    /* close 로직*/
    }
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean(destroyMethod = "destroy")
    public ShutdownHookConfiguration shutdownHookConfiguration() {
        return new ShutdownHookConfiguration();
    }

}
ShutdownHookConfiguration 클래스를 생성하고 destroy 메소드를 선언한 후 close 로직을 작성합니다.
그런 다음 SpringBootAplication 어노테이션을 등록한 클래스에 ShutdownHookConfiguration 클래스를 Bean으로 등록한 다음 destroyMethod에 destory메소드를 등록해줍니다.

pom.xml에 아래와 같은 코드를 입력하면 됩니다.
    <build>
     
        <plugins>
        ...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        ...
        </plugins>
  
    </build>

 

1. 데이터 정의어(DDL) 이란

테이블을 생성, 삭제하거나 컬럼을 추가, 수정, 삭제할 때 사용하는 명령어입니다.
크게 CREATE, ALTER, DROP, TRUNCATE로 구분됩니다.

 

2. 데이터 정의어 명령어 정리

1) CREATE

데이터베이스 또는 테이블을 생성할 때 사용합니다. 
create database 데이터베이스명;

show databases;

use 데이터베이스명;
create table 테이블명 (
       컬럼명 자료형 [제약조건 및 옵션],
       [키 제약조건 선언]
)
CREATE TABLE department (
	id INT(11) NOT NULL AUTO_INCREMENT,
	name VARCHAR(50) NULL DEFAULT NULL,
	PRIMARY KEY (id)
)

create table member(
  id int auto_increment not null,
  name varchar(50) not null,
  dept_id int null,
  primary key(id),
  constraint fk_department foreign key (dept_id) references department (id)
);
department 테이블과 member 테이블을 생성했습니다. member 테이블은 department의 id를 참조하고 있습니다.

 

2) 컬럼 옵션

auto_increment : 숫자 형에 사용가능 하며 자동으로 숫자가 증가하는 속성을 부여합니다. 
not null : null 값을 가질 수 없습니다.
unique : unique key 속성을 부여합니다. 해당 컬럼은 중복된 값을 가질 수 없습니다. 
default : 기본 값을 지정합니다.
check :  특정 값만 가질 수 있게 지정합니다.
primary key : 기본키로 지정합니다.
foreign key : 외래키로 지정합니다.
#컬럼 옵션 예제
create table member (
	id int auto_increment primary key,
	name varchar(50) not null,
	code char(1) not null default 'A',
	email varchar(100) unique,
	class char(1) check(class in (1,2,3,4)),
	dept_id int null,
	constraint fk_department foreign key (dept_id) references department (id)
)

3) ALTER

테이블을 수정할 때 사용하는 명령어 입니다.

3-1) 컬럼 추가

alter table 테이블명 add 컬럼명 컬럼타입 [옵션]

alter table member add column age int not null; 

3-2) 컬럼 수정

alter table 테이블명 modify 컬럼명 변경할데이터타입 [옵션]

alter table member modify name varchar(300) null

3-3) 컬럼 삭제

alter table 테이블명 drop 삭제할컬럼명

alter table member drop age;

3-4) 컬럼명 변경

alter table 테이블명 change 변경할컬럼명 새로운컬럼명 컬럼타입

alter table member change name title varchar(100);

3-5) 제약 조건 추가

alter table 테이블명 add constraint 제약조건명 제약조건(컬럼명..)

#유니크 제약조건 추가
alter table member add constraint uk_code_name unique key (code, name);

#기본키 제약조건 추가
alter table member add constraint pk_id_code primary key (id, code)

#외래키 제약조건 추가
#외래키를 지정할 경우 해당 컬럼에 index key가 함께 선언된다.
alter table member add constraint fk_dept_id foreign key (dept_id) references department (id)

3-6) 제약 조건 삭제

alter table 테이블명 drop constraint 제약조건명

#유니크 키 제거
alter table member drop constraint uk_code_name;

#외래키 제거
alter table member drop foreign key fk_dept_id

#인덱스 제거
alter table member drop constraint fk_dept_id

#제약조건명 조회방법
select * 
from information_schema.table_constraints
where TABLE_SCHEMA = '데이터베이스명' and TABLE_NAME = '테이블명'

3-7) 테이블 명 변경

alter table 테이블명 rename 새로운테이블명

alter table member rename customer;

 

4) DROP

데이터베이스 또는 테이블을 삭제할 때 사용합니다. 
drop database 데이터베이스명

drop database my_db;
drop table 테이블명

drop table member

 

5) TRUNCATE

테이블의 모든 내용을 지울 때 사용합니다.
truncate table 테이블명;

truncate table member;

 

'IT > Mysql & MariaDB' 카테고리의 다른 글

[Mysql] 조인 (JOIN) 이란?  (0) 2021.04.03
[Mysql] distinct와 group by의 차이  (0) 2021.03.28
[Mysql] group by와 having  (1) 2021.03.28
[Mysql] group by 시 가장 큰 값을 가진 row 조회하기  (0) 2021.03.27
[Mysql] delete join  (0) 2021.02.25

+ Recent posts