PostgreSQL의 체크 제약 조건(Check Constratint)에 정규표현식 적용하기

PostgreSQL은 매우 강력하고 뛰어나며 안정적인 DBMS입니다. 특히 공간 데이터 처리에도 뛰어난 면모를 보여줍니다. 2021년 DBMS 순위에서 상위권을 수년간 유지하고 있습니다. 1위는 오라클, 2위는 MySQL, 3위는 MS SQL Server이며 4위가 PostgreSQL입니다. 사용 빈도는 타 DBMS와 비교하여 지속적으로 증가하고 있습니다. 특히 유연성과 확장성을 중시하는 북미와 안정성에 보수적인 일본에서 PostgreSQL의 사용율이 매우 높습니다. 엔터프라이즈 DB의 이미지를 강조하기 위해 로고를 코끼리로 표현하고 있습니다.

사용자가 입력한 값을 프로그래밍 코드를 통해서도 유효한지 검사를 해야 하고 실제 서버 단의 REST API에서도 전달받은 값이 유효 한지를 검사해야하며 DBMS에 실제 값이 입력되는 SQL 호출에서도 값이 유효 한지를 검사해야합니다.

이때 입력한 값이 어떤 정해진 규칙을 준수하고 있는지에 대한 검사가 필요할 때가 있습니다. 예를들어서 메일주소나 핸드폰번호에 대한 값이 있습니다. 이러한 규칙은 흔히 정규표현식으로 지정할 수 있습니다. 정규표현식은 개발자라면 반드시 익혀야 하는 도구입니다.

다음은 DBMS에 INSERT 문을 통해 데이터를 추가할 때 값이 정해진 규칙으로 정규표현식 등에 맞는지 검사하도록 테이블 생성시 제약조건을 걸어준 예입니다.

CREATE TABLE "user" (
    id VARCHAR(20) PRIMARY KEY, -- 사용자 ID
    name VARCHAR(20) NOT NULL,
    mobilephone VARCHAR(13) NOT NULL,
    email VARCHAR(40) NOT NULL,
    CONSTRAINT chk_id CHECK (CHAR_LENGTH(id) >= 3),
    CONSTRAINT chk_name CHECK (CHAR_LENGTH(id) >= 3),
    CONSTRAINT chk_email CHECK (email ~* '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+[.][A-Za-z]+$'),
    CONSTRAINT chk_mobilephone CHECK (mobilephone ~* '^01[0|1|6|7|8|9]-[0-9]{3,4}-[0-9]{4}$')
);

NULL 값 입력 방지, 문자열에 대한 일 길이 이상, 그리고 메일이나 전화번호 등의 형식에 맞는 값인지를 정규표현식을 이용해 검사합니다.

PostgreSQL의 외래키 제약조건(Foreign Key Constraint)

외래키는 외부 테이블의 어떤 필드의 값을 참조함으로써 1:N, N:N, N:1과 같은 관계를 맺어주는 키 필드입니다.

외래키로 참조하고 있는 외부 테이블의 값이 변경되거나 제거되면 DB에 논리적인 오류가 남게 됩니다. 그래서 기본적으로 외부 테이블의 값이 변경되거나 제거를 하기 전에 외래키를 검사해서 문제가 없을 경우에만 변경 또는 제거를 수행하게 됩니다.

우리가 원하는 것은 외부 테이블의 참조 값이 변경되거나 삭제될때 외래키에 해당되는 값도 그에 맞게 변경시켜주거나 삭제해줘야 합니다. 이때 외래키에 대한 제약조건을 걸때 다음처럼 해주면 됩니다.

먼저 다음과 같은 2개의 테이블이 있습니다.

user 테이블의 생성은 다음과 같습니다.

CREATE TABLE "user" (
    id VARCHAR(20) PRIMARY KEY, -- 사용자 ID
    name VARCHAR(20),
	CONSTRAINT chk_id CHECK (CHAR_LENGTH(id) >= 5)
);

그리고 이 user의 id를 참조하는 coin 테이블의 생성은 다음과 같습니다.

CREATE TABLE coin (
	id SERIAL PRIMARY KEY,
    user_id VARCHAR(20) NOT NULL, -- 사용자 ID
	amount INTEGER,
    CONSTRAINT fk_user_id FOREIGN KEY(user_id) REFERENCES "user"(id) ON DELETE CASCADE ON UPDATE CASCADE
);

즉 user 테이블의 id 값을 coin 테이블의 user_id에서 외래키로 참조하도록 제약 조건을 걸고 있습니다. 이때 ON DELETE CASCADE ON UPDATE CASCADE를 걸어주면 됩니다. 만약 이 조건(ON DELETE CASCADE ON UPDATE CASCADE)을 지정하지 않으면 제약 검증을 실패할 경우 삭제 또는 수정이 되지 않습니다.

웹에서 File 선택하기(파일 열기 대화상자 or 파일 드래그&드랍)

웹에서 파일을 선택하기 위해서는 사용자의 행위가 필요한데, 파일 열기 대화상자를 통해 파일을 선택해 주거나 파일을 드래그해서 웹브라우저에 드랍해줘야합니다.

먼저 파일을 드랍해서 떨굴 영역을 마련해 줍니다.

이곳을 클릭하거나
이곳에 파일을 드래그 하세요.

.file-zone에 대한 div 영역이 파일을 드랍해서 떨굴 영역입니다.

JS 코드는 다음과 같습니다.

fileZone.addEventListener("drop", (event) => {
    const files = event.dataTransfer.files;
    const file = files[0];
    if(file) {
        processFile(file);
    }

    event.preventDefault();
});

fileZone.addEventListener("dragover", (event) => {
    event.preventDefault();
});

processFile은 떨군 파일을 처리하는 함수인데요. 예를 들면 다음과 같습니다.

function processFile(file) {
    const name = file.name;
    const size = file.size;
    const reader = new FileReader();

    reader.onload = function() {
        const aBuf = this.result; // ArrayBuffer
        //const dView = new DataView(aBuf);
        //const validFlag = dView.getUint8(0);
        //const numRecords = dView.getInt32(4, true);

        const MD5 = CryptoJS.MD5(aBuf).toString();
        const SHA256 = CryptoJS.SHA256(aBuf).toString();
        
        gwcMessage(`Size ${size} bytes
MD5 ${MD5}
SHA256 ${SHA256}`); }; const blob = file.slice(0, size); reader.readAsArrayBuffer(blob); }

이제 파일 열기 대화상자를 통해 file을 선택해 처리해보겠습니다. 앞서 HTML 코드를 보면 file 타입의 input tag가 있는데 이 DOM 요소를 통해 파일 열기 대화상자를 표시합니다. 이 DOM 요소의 display는 none으로 해서 화면에 보이지 않도록 하는게 일반적입니다.

JS 코드는 다음과 같습니다.

const fileZone = document.querySelector(".file-zone");

fileZone.addEventListener("click", () => {
    document.querySelector("input").click();
});

document.querySelector("input").onchange = function() {
    const file = this.files[0];
    if(file) {
        processFile(file);
    }                
};

GWC 라이브러리 – Quick Start Page

GWC 라이브러리의 UI를 이용해 샘플 페이지를 개발할 때 사용하는 시작 코드입니다. 참조하세요.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <style>
            @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap');

            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
                font-family: 'Noto Sans KR';
            }

            body {
                padding: 0;
                margin: 0;
                width: 100%;
                height: 100vh;
                background-color: rgb(30,30,30);
            }

            #main-layout {
                width: 100%;
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
                opacity: 0.001;
                transition: 2s opacity;
            }

            #main-layout.show {
                opacity: 1;
            }
        </style>

        <link rel="stylesheet" href="../js/gwc/geoservice-web-components.css">
        <script src="../js/gwc/geoservice-web-components.js" defer>
        <title>GWC Library Quick Start Page</title>
    </head>
    <script>
        window.addEventListener("load", () => {
            document.querySelector("#main-layout").classList.add("show");
        });

        window.addEventListener("DOMContentLoaded", () => {
            GeoServiceWebComponentManager.instance.update();
        });
    </script>
    <body>
        <div id="main-layout">
        </div>
    </body>
</html>

Page에 대한 UI 구성이 모두 완료되면 서서히 나타나도록 하는 효과가 포함되어 있습니다. 만약 Page에 UI가 그렇게 많지 않다면 이 효과는 제거 시켜도 됩니다.