개발 시작하기
개발을 시작하기 전에
우리는 이 과정을 통해 직원 목록을 보는 페이지를 만들겁니다. ProjectRoom으로 Web Application을 만드는 기본적인 사항들을 배웁니다.
팁
이 과정은 실습으로 배우기를 선호는 사람들에 맞춰 가이드 되었습니다. 더 깊은 내용을 원하시면 단계별 가이드를 확인해보세요.
무엇을 만들게 될까요?

필요한 선수 지식
REST API, Client Side Rendering(CSR), Server Side Rendering(SSR)의 개념에 대해 익숙하다고 가정합니다.
팁
ProjectRoom은 AWS 인프라(EC2, EFS, API Gateway, S3 등)를 활용해 CSR을 지원하고 있습니다.
현재 지원하고 있는 SPA(Single Page Application) 프레임워크는 React, Vue, Svelte가 있습니다.
요청은 어떻게 해야할까요?
처음 접근해야하는 파일을 알아보겠습니다.
우선, 워크스페이스의 왼쪽 트리를 확인합니다.
SERVER -> src -> main -> java -> projectroom -> module

위의 module 폴더 아래에 자바 파일을 생성하면 됩니다.
주의할 점
module 폴더 아래에 위치해야 URL Mapping을 하는 대상 파일이 됩니다.
자, 그러면 Emp 자바 클래스를 만들어 보겠습니다.
package projectroom.module;
import io.projectroom.framework.vo.AppContext;
import io.projectroom.framework.vo.DataItem;
import io.projectroom.framework.annotation.Module;
import java.util.ArrayList;
import java.util.List;
@Module
public class Emp {
public List<DataItem> getNames(DataItem parameter, AppContext appContext) {
List<DataItem> list = new ArrayList<>();
list.add(new DataItem().append("name", "홍길동"));
list.add(new DataItem().append("name", "둘리"));
return list;
}
}module 폴더 아래의 java 클래스 중 @Module 어노테이션이 붙은 클래스의 메소드명을 대상으로 URL Mapping이 됩니다.
주의할 점
클래스의 위에 명시된 @Module 어노테이션은 꼭 붙이셔야합니다.
SERVER -> module

위의 module 폴더 아래에 Node 파일을 생성하면 됩니다.
주의할 점
module 폴더 아래에 위치해야 URL Mapping을 하는 대상 파일이 됩니다.
자, 그러면 User 파일을 만들어 보겠습니다.
exports.getUserById = async (param, context) => {
const dao = context.dao;
return await dao.select("user.selectUserById", param);
};
exports.getUserByName = async (param, context) => {
const dao = context.dao;
return await dao.select("user.selectUserByName", param);
};
exports.createUser = async (param, context) => {
const dao = context.dao;
dao.insert("user.insertUser", param);
};
exports.modifyUser = async (param, context) => {
const dao = context.dao;
dao.update("user.updateUser", param);
};
exports.deleteUser = async (param, context) => {
const dao = context.dao;
dao.delete("user.deleteUser", param);
}
exports.getUserByTest = async (param, context) => {
const array = new Array();
array.push({ "id":1, "name": "고길동", "email": "[email protected]" });
array.push({ "id": 2, "name": "둘리", "email": "[email protected]" });
return array;
}직접 DB에서 데이터를 활용하고 싶으시다면 데이터 접근하기를 참고하시면 됩니다.
GET /api/User/getUserByName
테스트는 어떻게 해야할까요?
워크스페이스 우측 상단의 API Tester를 클릭하여 만들어진 API를 확인해봅니다.

API Tester를 누릅니다.
User를 클릭하면 해당 모듈에서 만들어둔 API 목록이 보여집니다.
테스트 하고자 하는 API를 클릭합니다.
요청에 맞는 Parameter를 넣어줍니다.
Send 버튼을 눌러 API 요청을 합니다.
응답 결과가 제대로 오는지 확인합니다.
화면은 어떻게 보여줄지 알아봅시다
자, API를 만들었고, 기본적인 테스트를 진행하였습니다. 만들어진 해당 API를 클라이언트 단에선 어떻게 사용할까요?
처음 접근해야 하는 파일을 알아봅시다.
우선, 워크스페이스의 왼쪽 트리를 확인합니다. index.html 파일이 첫화면이 되는 파일입니다.
UI -> public -> index.html

UI -> public -> index.html

UI -> public -> index.html

UI -> index.html

현재 개발된 첫화면을 보려면 어떻게 할까요?
첫화면에 접근하기 위해선 우측 상단의 Web Tester를 클릭하면 됩니다.

아래와 같이 각각 부여된 Web Tester 주소를 통해 개발중인 화면(index.html)에 접근을 할 수 있게 됩니다

자, 이제 예제 index 페이지를 구성해보겠습니다. 아래의 코드를 넣고 복사 붙여넣기를 합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Javascript app</title>
<link rel="icon" type="image/png" href="./static/favicon.ico" />
<link rel="stylesheet" href="./static/build/bundle.css" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
</script>
<script src="./static/build/bundle.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="container-xxl bd-gutter mt-3 my-md-4 bd-layout">
<div class="bd-main order-1">
<h2>기본적인 CRUD</h2>
<div class="section">
<div class="form-row d-md-flex justify-content-md-end">
<button type="button" class="btn btn-primary btn-sm me-md-1" id="list-refresh">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
</button>
<button type="button" class="btn btn-success btn-sm me-md-1" id="add-buttton">Create</button>
<form class="d-flex" role="search">
<div class="input-group">
<input class="form-control" id="search-area"type="search" placeholder="Search" aria-label="Search"/>
<button type="button" class="btn btn-outline-secondary" id="search-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path>
</svg>
</button>
</div>
</form>
</div>
<div class="modal fade" id="add-modal" tabindex="-1" aria-labelledby="add-modal-label"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="add-modal-label">
사용자 추가
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
</div>
<div class="modal-body">
<div class="mb-3 row">
<label for="inputPassword" class="col-sm-2 col-form-label user-name">이름</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="name" />
</div>
</div>
<div class="mb-3 row">
<label for="inputPassword" class="col-sm-2 col-form-label user-email">이메일</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="email" />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="modal-add-button">Save changes</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modify-modal" tabindex="-1" aria-labelledby="modify-modal-label"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modify-label">사용자 변경</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3 row">
<label for="inputId" class="col-sm-2 col-form-label user-id">아이디</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="id" disabled readonly/>
</div>
</div>
<div class="mb-3 row">
<label for="inputPassword" class="col-sm-2 col-form-label user-name">이름</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="name" />
</div>
</div>
<div class="mb-3 row">
<label for="inputPassword" class="col-sm-2 col-form-label user-email">이메일</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="email" />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="modal-modify-button">Save changes</button>
</div>
</div>
</div>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="list-area" />
</table>
</div>
<h2>파일 업로드</h2>
<div class="section">
<div class="input-group">
<input type="file" class="form-control" id="upload-area" aria-describedby="inputGroupFileAdd" aria-label="Upload" multiple/>
<button class="btn btn-outline-secondary" type="button" id="upload-button">Upload File</button>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Size</th>
</tr>
</thead>
<tbody id="file-area" />
</table>
</div>
</div>
</div>
</div>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="manifest.json" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
</script>
<script defer src='/static/build/bundle.js'></script>
<link rel="stylesheet" href="/static/build/bundle.css">
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App/>
</React.StrictMode>
);<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/static/favicon.png'>
<link rel='stylesheet' href='/static/global.css'>
<link rel='stylesheet' href='/static/build/bundle.css'>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script defer src='/static/build/bundle.js'></script>
</head>
<body>
</body>
</html>import App from './App.svelte';
var app = new App({
target: document.body
});
export default app;<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>import {createApp} from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
index.html의 정적인 부분을 채워 넣었으니, 이제 동적인 작업을 해줄 index.js 부분도 마저 작업해봅시다.
index.html 페이지가 제대로 나오고 있는지 확인합니다.
시작점이 되는는 index.html 페이지가 제대로 나온다면, 아래의 javascript 코드를 만들어봅시다.
import "./index.css";
import CRUD from "./crud.js";
import Upload from "./upload.js";
$(function () {
const crudData = {
listRefresh: $("#list-refresh"),
addButton: $("#add-buttton"),
searchArea: $("#search-area"),
searchButton: $("#search-button"),
listArea: $("#list-area"),
addModal: $("#add-modal"),
modifyModal: $("#modify-modal"),
modalAddButton: $("#modal-add-button"),
modalModifyButton: $("#modal-modify-button"),
};
new CRUD(crudData);
const uploadData = {
uploadArea: $("#upload-area"),
uploadButton: $("#upload-button"),
fileArea: $("#file-area"),
};
new Upload(uploadData);
});
import { PRConnect } from "@projectroom/ui-lib";
export default class CRUD {
constructor({
listRefresh,
addButton,
searchArea,
searchButton,
listArea,
addModal,
modifyModal,
modalAddButton,
modalModifyButton,
}) {
this.listRefresh = listRefresh;
this.addButton = addButton;
this.searchArea = searchArea;
this.searchButton = searchButton;
this.listArea = listArea;
this.addModal = addModal;
this.modifyModal = modifyModal;
this.modifyModal = modifyModal;
this.modalAddButton = modalAddButton;
this.modalModifyButton = modalModifyButton;
this._initEventListener();
this._getData();
}
_initEventListener() {
this.listRefresh.click((e) => {
this.searchArea.val("");
this._getData();
});
this.modalAddButton.click((e) => {
this._addUser(e);
});
this.addButton.click(() => {
this.addModal.modal("show");
});
this.modalModifyButton.click((e) => {
this._modifyUser(e);
});
this.listArea.on("click", "button", (e) => {
const action = $(e.target).data("action");
const $targetNode = $(e.target).closest("tr");
switch (action) {
case "modify":
this.modifyModal
.find("input[name='id']")
.val($targetNode.find("#user-id").text());
this.modifyModal
.find("input[name='name']")
.val($targetNode.find("#user-name").text());
this.modifyModal
.find("input[name='email']")
.val($targetNode.find("#user-email").text());
this.modifyModal.modal("show");
break;
case "delete":
this._deleteUser(e);
break;
}
});
this.searchArea.on("keyup", (e) => {
console.log(e.target.value);
if (e.key === "Enter") {
e.preventDefault();
this._getData();
this.searchButton.focus();
}
});
this.searchButton.click((e) => {
e.preventDefault();
this._getData();
});
}
_getData() {
debugger;
let param = { name: this.searchArea.val() };
PRConnect.send("/api/User/getUserByName", param, (data) => {
this._makeList(data.result);
});
}
_addUser(e) {
const $targetNode = $(e.currentTarget).closest(".modal-content");
const param = {
name: $targetNode.find("input[name='name']").val(),
email: $targetNode.find("input[name='email']").val(),
};
PRConnect.send("/api/User/createUser", param, (data) => {
if (data.status === "success") {
alert("추가 되었습니다.");
this._getData();
} else {
alert("실패 하였습니다.");
}
});
this.addModal.modal("hide");
$targetNode.find("input").each(function (index, item) {
$(item).val("");
});
}
_modifyUser(e) {
const $targetNode = $(e.currentTarget).closest(".modal-content");
const param = {
id: $targetNode.find("input[name='id']").val(),
name: $targetNode.find("input[name='name']").val(),
email: $targetNode.find("input[name='email']").val(),
};
PRConnect.send("/api/User/modifyUser", param, (data) => {
if (data.status === "success") {
alert("수정 되었습니다.");
this._getData();
} else {
alert("실패 하였습니다.");
}
});
this.modifyModal.modal("hide");
$targetNode.find("input").each(function (index, item) {
$(item).val("");
});
}
_deleteUser(e) {
const $targetNode = $(e.target).closest("tr");
const id = $targetNode.find("#user-id").text();
PRConnect.send("/api/User/deleteUser", { id }, (data) => {
if (data.status === "success") {
alert("삭제 되었습니다.");
this._getData();
} else {
alert("삭제 실패 하였습니다.");
}
});
}
_makeList(list) {
this.listArea.empty();
this.listArea.append(this._listItemTemplate(list));
}
_listItemTemplate(list) {
return list
?.map((user) => {
return `<tr>
<td id="user-id">${user.id}</td>
<td id="user-name">${user.name}</td>
<td id="user-email">${user.email}</td>
<td>
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-outline-primary" data-action="modify">수정</button>
<button type="button" class="btn btn-secondary" data-action="delete">삭제</button>
</div>
</tr>`;
})
.join("");
}
}import { PRFile } from "@projectroom/ui-lib";
export default class Upload {
constructor({ uploadArea, uploadButton, fileArea }) {
this.uploadArea = uploadArea;
this.uploadButton = uploadButton;
this.fileAreaja= fileArea;
this._initEventListener();
}
_initEventListener() {
this.uploadButton.click((e) => {
this._uploadFile();
});
}
_uploadFile() {
const root = "/uploadedFiles";
const files = this.uploadArea[0].files;
PRFile.uploadFiles(root, files, () => {
const list = this._listItemTemplate(files);
this.fileArea.append(list);
});
}
_listItemTemplate(files) {
return Array.from(files)
?.map((file) => {
return `<tr>
<td>${file.name}</td>
<td>${file.size}</td>
</tr>`;
})
.join("");
}
}index.html 페이지가 제대로 나오고 있는지 확인합니다.
시작점이 되는는 index.html 페이지가 제대로 나온다면, 아래의 스크립트 코드 전부를 App.js 안에 복사 붙여넣기 합니다.
import { PRConnect, PRFile } from "@projectroom/ui-lib";
import React, { useState } from 'react';
function App() {
const [userList, setUserList] = useState([]);
const [inputFileList, setInputFileList] = useState([]);
const [fileList, setFileList] = useState([]);
const clear = () => {
setUserList([]);
}
const getData = () => {
let param = {};
PRConnect.send("/api/User/getUsers", param, data => {
setUserList(data.result);
});
}
const uploadSetting = (event) => {
setInputFileList(event.target.files);
}
const uploadFile = () => {
let root = "/uploadedFiles";
let files = inputFileList;
PRFile.uploadFiles(root, files, () => {
for (let file of files) {
setFileList(fileList => [ ...fileList, { name: file.name, size: file.size }]);
}
});
}
return (
<div className="App">
<h1>Hello</h1>
<button type="button" class="btn btn-secondary btn-sm" onClick={clear}>Clear</button>
<button type="button" class="btn btn-primary btn-sm" onClick={getData}> Search</button >
<table class="table" >
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
{userList.map((user) => {
return (<tr>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>)
})}
</tbody>
</table>
<input onChange={uploadSetting} type="file" multiple />
<button type="button" class="btn btn-primary btn-sm" onClick={uploadFile}> Upload File</button>
<table class="table" >
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Size</th>
</tr>
</thead>
<tbody>
{fileList.map((file) => {
return (
<tr>
<td>{file.name}</td>
<td>{file.size}</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
}
export default App;index.html 페이지가 제대로 나오고 있는지 확인합니다.
시작점이 되는는 index.html 페이지가 제대로 나온다면, 아래의 스크립트 코드 전부를 App.svelte 안에 복사 붙여넣기 합니다.
<script>
import {onDestroy, onMount} from 'svelte';
import {PRConnect, PRFile} from "@projectroom/ui-lib";
let name = 'world';
let userList = [];
let fileList = [];
let inputFile;
onMount( async () => {
});
onDestroy(() => {
});
const clear = () => {
userList = [];
}
const getData = () => {
let param = {};
PRConnect.send("/api/User/getUsers", param, data => {
userList = data.result;
});
}
const uploadFile = () => {
let root = "/uploadedFiles";
let files = inputFile.files;
fileList = [];
PRFile.uploadFiles(root, files, data =>{
for( let file of files ) {
fileList = [...fileList, { name : file.name, size : file.size }];
}
});
}
</script>
<h1>Hello {name}!</h1>
<button type="button" class="btn btn-secondary btn-sm" on:click={clear}>Clear</button>
<button type="button" class="btn btn-primary btn-sm" on:click={getData}>Search</button>
<table class="table" >
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
{#each userList as user}
<tr>
<th scope="row">{user.id}</th>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>
{/each}
</tbody>
</table>
<input bind:this={inputFile} type="file" multiple >
<button type="button" class="btn btn-primary btn-sm" on:click={uploadFile}>Upload File</button>
<table class="table" >
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Size</th>
</tr>
</thead>
<tbody>
{#each fileList as file}
<tr>
<td>{file.name}</td>
<td>{file.size}</td>
</tr>
{/each}
</tbody>
</table>
<style>
h1 { color : red; }
</style>index.html 페이지가 제대로 나오고 있는지 확인합니다.
시작점이 되는는 index.html 페이지가 제대로 나온다면, 아래의 스크립트 코드 전부를 App.vue 안에 복사 붙여넣기 합니다.
<script setup>
import {PRConnect, PRFile} from "@projectroom/ui-lib";
</script>
<template>
<h1>Hello!!!</h1>
<button type="button" class="btn btn-secondary btn-sm" @click="clear">Clear</button>
<button type="button" class="btn btn-primary btn-sm" @click="getData">Search</button>
<table class="table" >
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr v-for="user in userList">
<th scope="row">{{user.id}}</th>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
</tr>
</tbody>
</table>
<input @change="uploadSetting" type="file" multiple >
<button type="button" class="btn btn-primary btn-sm" @click="uploadFile">Upload File</button>
<table class="table" >
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Size</th>
</tr>
</thead>
<tbody>
<tr v-for="file in fileList">
<td>{{file.name}}</td>
<td>{{file.siz}}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
created () {
PRConnect.send("/api/User/getUsers", {}, data => {
this.userList = data.result;
});
},
name: "",
data: function () {
return {
userList : [],
inputFileList : [],
fileList : []
}
},
methods: {
clear() {
this.userList = [];
},
getData() {
let param = {};
PRConnect.send("/api/User/getUsers", param, data => {
this.userList = data.result;
});
},
uploadSetting(event) {
this.inputFileList = event.target.files;
},
uploadFile() {
let root = "/uploadedFiles";
let files = this.inputFileList;
PRFile.uploadFiles(root, files, data =>{
for( let file of files ) {
this.fileList = [...this.fileList, { name : file.name, size : file.size }];
}
});
}
}
}
</script>위와 같은 패턴으로 'API(서버)'와 'Client(화면)' 간의 데이터를 주고 받으시면 됩니다.
만들고 하는 화면이 완성되었습니다.🥳🥳🥳🥳🥳🥳
Last updated