CryptoJS를 이용한 AES256 암호화 / 복호화

자바스크립트에서 CryptoJS 라이브러리를 사용하여 AES256 방식으로 데이터를 암호화하기 위한 함수는 다음과 같습니다.

encodeByAES56(key, data){
    const cipher = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(""),
        padding: CryptoJS.pad.Pkcs7,
        mode: CryptoJS.mode.CBC
    });

    return cipher.toString();
}

위의 함수를 사용하는 예는 다음과 같구요.

const k = "key";
const rk = k.padEnd(32, " "); // AES256은 key 길이가 32자여야 함
const b = "암호화는 보안을 위해 매우 중요합니다.";
const eb = this.encodeByAES56(rk, b);

console.log(eb);

출력 결과는 다음과 같습니다.

mXK9nlcT70QdTjKgzxAD99zS4UYah0OZI8GFT8Pg+Vu3GFmF/HPmlV0PA/sUy7rr4+2Sh319ZEIz2TlyiPWcvw==

암호화된 위의 문자열을 복호화하는 함수는 다음과 같습니다.

decodeByAES256(key, data){
    const cipher = CryptoJS.AES.decrypt(data, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(""),
        padding: CryptoJS.pad.Pkcs7,
        mode: CryptoJS.mode.CBC
    });

    return cipher.toString(CryptoJS.enc.Utf8);
};

위의 함수를 사용하는 예는 다음과 같구요.

const k = "key"; // 암호화에서 사용한 값과 동일하게 해야함
const rk = k.padEnd(32, " "); // AES256은 key 길이가 32자여야 함
const eb = "mXK9nlcT70QdTjKgzxAD99zS4UYah0OZI8GFT8Pg+Vu3GFmF/HPmlV0PA/sUy7rr4+2Sh319ZEIz2TlyiPWcvw==";
const b = this.decodeByAES56(rk, eb);

console.log(b);

실행 결과는 다음과 같습니다.

암호화는 보안을 위해 매우 중요합니다.

SharedArrayBuffer 사용을 위한 Live-Server 서버 설정

일단 SharedArrayBuffer API를 웹브라우저에서 사용하기 위해서는 다음과 같은 해더값을 웹서버에 추가해줘야 합니다.

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Tomcat이나 Apache 등과 웹서버가 아닌 VS.Code의 확장 기능으로써의 웹 서버인 Live-Server에서 위의 해더값을 추가하기 위해서 먼저 live-server가 설치된 폴더(필자의 경우 C:\Users\GEOSERVICE-PT\.vscode\extensions\ritwickdey.liveserver-5.7.5\node_modules\live-server)의 index.js 파일을 열고 “if (cors) {” 문자열에 대한 코드를 찾은 후 이 코드 바로 직전에 다음 코드를 추가해주면 됩니다.

app.use((req, res, next) => {
    res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
    res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
    next();
});

#GWC UI Library : Space

웹 UI 라이브러리인 GWC에서 제공하는 Space 컴포넌트에 대한 예제 코드입니다. 이 컴포넌트는 UI 간의 여백을 지정하기 위한 목적으로 사용됩니다.

먼저 DOM 구성을 위한 JS 코드는 다음과 같습니다.

const domLayout = document.createElement("div");
domLayout.classList.add("login");

domLayout.innerHTML = `
    
`;
document.body.appendChild(domLayout);

Space 컴포넌트는 gw-space라는 Tag로 구성될 수 있는데, 여백에 대한 크기를 10px 또는 20px,10px 등으로 나타낼 수 있습니다. 10px의 경우 가로와 세로 모두에 대한 여백의 크기이고 20px,10px은 가로와 세로에 대한 크기를 서로 다르게 지정합니다. 위의 코드에 대한 결과는 다음과 같습니다.

이 컴포넌트에 대한 CSS 코드는 필요치 않으나 위의 코드에서 사용된 CSS는 다음과 같습니다.
.login {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background: black;
}

.login .login-form {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: inline-flex;
    flex-flow: column;
    gap: 0.5em;
    background: rgba(255,255,255,0.1);
    padding: 4em 4em 1.5em 4em;
    border-radius: 1em;
    box-shadow: -0.6px -0.6px 0.6px rgba(255,255,255,0.3);
}

.login .login-form gwc-textinput {
    width: 300px;
}

.login .login-form .login-title {
    font-family: Raleway;
    color: white;
    font-size: 2em;
}

.login .login-form .login-form-tools {
    display: flex;
    justify-content: center;
    align-items: center;
    zoom: 0.9;
}

GWC 라이브러리, UI 구성 예 (gwcCreateModalDialog, Tree 등)

다음과 같은 UI 구성을 목표라고 할때…

대화상자 형태의 레이아웃이므로 gwcCreateModalDialog API를 이용한 클래스를 작성합니다.

class DownloadLayerList {
    constructor() {
        const dlg = gwcCreateModalDialog("레이어 목록 불러오기");
        dlg.content = `
            <div class="download-layer-list-dialog h-center content">
                <div class="vertical-linear-layout">
                    <div class="horizontal-linear-layout v-center">
                        <gwc-label content="필터" outline-type="none"></gwc-label>
                        <gwc-textinput hint="필터링할 값을 입력하세요."></gwc-textinput>
                        <gwc-toolbutton class="refresh" icon="../images/reset.svg"></gwc-toolbutton>
                    </div>
                    <gwc-vscrollview>
                        <content>
                            <gwc-tree></gwc-tree>
                        </content>
                    </gwc-vscrollview>
                    <div class="horizontal-linear-layout h-center v-space"> 
                        <gwc-button class="btnConfirm" title="불러오기" disabled=true></gwc-button>
                        <gwc-button class="btnCancel" title="취소"></gwc-button>
                    </div>
                </div>                    
            </div>
        `;

        dlg.show();
        GeoServiceWebComponentManager.instance.update();

        ....

대화상자의 크기 조정이 필요하다면 아래의 코드를 추가합니다.

        dlg.width = "30em"
        dlg.resizablePanel.resizableLeft = true;
        dlg.resizablePanel.resizableRight = true;
        dlg.resizablePanel.resizableTop = true;
        dlg.resizablePanel.resizableBottom = true;
        dlg.resizablePanel.minWidth = 350;
        dlg.resizablePanel.minHeight = 300;
        dlg.resizablePanel.addEventListener("change", (event) => {
            const { mode, oldHeight, newHeight } = event.detail;
            if(mode === "BOTTOM" || mode == "TOP") {
                const vscrollview = dlg.content.querySelector("gwc-vscrollview");
                const height = parseFloat(window.getComputedStyle(vscrollview).getPropertyValue("height"));
                vscrollview.style.height = `${height - (oldHeight - newHeight)}px`;
                vscrollview.refresh();
            }            
        });
    }

    ....

CSS에 대한 코드는 다음과 같습니다.


.download-layer-list-dialog {
    padding: 0.5em;
}

.download-layer-list-dialog gwc-textinput {
    flex: 1; /* 대화상자의 가로 크기를 재조정 했을 때 gwc-textinput의 크기도 재조정됨  */
}

.download-layer-list-dialog gwc-vscrollview {
    position: relative;
    height: 16em;
    background: rgba(0,0,0,0.8);
    border-radius: 0.5em;
    overflow: hidden;
}

.download-layer-list-dialog gwc-tree {
    width: 100%; /* tree의 항목의 크기를 대화상자의 폭을 가득 채우도록 조정함 */
    padding: 0.5em 0.5em 0.5em 0.5em;
}

.download-layer-list-dialog gwc-tree .gwc-tree-folder-files-file {
    width: 100%;
}

끝으로 gwc의 tree 컴포넌트는 동일한 계층에 동일한 이름을 가진 항목을 추가할 수 없습니다. 이때 label 속성을 이용해 이름은 다르지만 표시되는 제목은 중복되게 변경해 줄 수 있습니다. 코드 예시는 다음과 같습니다.

data.forEach(item => {
    const rootFolder = this.#tree.rootFolder;
    const file = rootFolder.addFile(item.id, "url(../images/layers.svg)");

    const date = new Date(item.used_time);
    file.tag = `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`;
    file.label = item.title;
});