안녕하세요 프로그래밍 맛보기 3부로 왔습니다.

1부에서는 프로그래밍의 Basic 개념에 대해 다루었었습니다

2부에서는 객체 지향 프로그래밍에서 중요한 개념인 Class에 대해 배웠었습니다

그리고 이번 3부에서는 우리가 공부한 것들을 바탕으로 서비스 단위 개발을 맛보기로 해볼까 합니다

 

2부에 작성했던 업무모델 로직을 바탕으로 진행하겠습니다.

여기에 서비스 화면UI, 서비스 처리 로직을 조금 더하여 만들어보겠습니다.

 

이번에도 메모장을 열어 다음 코드를 함께 작성하시겠습니다.

<script type="text/javascript">
//----- 서비스에 사용될 Data들 ---------------------------------------------------------------------------------------------------
var employees = [ //시스템에 등록된 사원목록
	{ 
		employee_id: '12345',
		name: '소영희', 
		position: '대리', 
		working_months: 36,
		salary: 4000
	}, 
	{ 
		employee_id: '67890',
		name: '장경식', 
		position: '과장', 
		working_months: 52,
		salary: 5300
	}, 
	{ 
		employee_id: '14703',
		name: '정명준', 
		position: '차장', 
		working_months: 85,
		salary: 6000
	}
];
var tasks = [ //시스템에 등재된 업무목록
	{  
		task_id: '54321',
		task: 'xx프로젝트 팀원',
		working_hours: 42,
		person_in_charge: '67890'
	}, 
	{  
		task_id: '09876',
		task: 'xx프로젝트 영업지원',
		working_hours: 57,
		person_in_charge: '12345'
	}
];
</script>
<!----- 서비스 화면UI ------------------------------------------------------------------------------------------------------->
<div class="data">
	<div class="dataTitle" >
		<b>[ㅇㅇ모직 직원목록]</b>
	</div>
	<table cellpadding="0" cellspacing="0" style="text-align:center;">
		<thead>
			<tr>
				<th></th><th>사번</th><th>이름</th><th>직위</th><th>근무개월수</th><th>연봉</th><th>담당업무명</th><th>공수</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td colspan="8">결과를 조회 중입니다</td>
			</tr><!--
			<tr>
				<td>b</td><td>b</td><td>b</td><td>b</td><td>b</td><td>b</td><td>b</td>
			</tr>
			<tr>
				<td>c</td><td>c</td><td>c</td><td>c</td><td>c</td><td>c</td><td>c</td>
			</tr>
			<tr>
				<td>d</td><td>d</td><td>d</td><td>d</td><td>d</td><td>d</td><td>d</td>
			</tr>-->
		</tbody>
	</table>
	<div>
		<button onclick="hrd.showEmployeeInfoModal('add')" >등록</button>
		<button onclick="hrd.showEmployeeInfoModal('modify')" >수정</button>
		<button onclick="hrd.deleteEmployeeInfoById()" >삭제</button>
	</div>
</div>

	<!-- The Modal -->
	<div id="myModal" class="fade-bg">
	  <!-- Modal content -->
	  <div class="modal-content">
	    <div class="close" style="float:right">&times;</div>
	    <p id="modalTitle1" class="modalTitle" style="float:left;"><b>사원 등록/수정</b></p>
	    <form><!-- serialize  -->
	    <table>
	    	<tr>
	    		<th width="30"></th><th width="100"></th><th></th>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td>사번</td><td><input type="text" name="employee_id"></td>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td>이름</td><td><input type="text" name="name"></td>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td>직위</td><td><input type="text" name="position"></td>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td>근무개월수</td><td><input type="text" name="working_months"></td>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td>연봉</td><td><input type="text" name="salary"></td>
	    	</tr>
	    	<tr>
	    		<td></td>
	    		<td colspan="2">
	    			<button type="button" id="modalBtn" onclick="" >등록</button>
	    			<button type="button" onclick="showModal('N')" >취소</button>
	    		</td>
	    	</tr>
	    </table> 
	   	</form>
	  </div>
	</div>

	<!-- The Loader -->
	<div id="myLoader" class="fade-bg">
	  <div class="loader"></div>
	</div>

<style type="text/css">

button { float:left;width:150;padding:10 10;margin:30 20; }

.data { padding:30 100;width:600 } 
.dataTitle { font-size:15pt;padding:20 0; }

div.data table { text-align:center;width:850; }
div.data table th { padding:10 15;background-color:black;color:white; } 
div.data table td { padding:10 15; }
div.data tr:nth-child(n) td { background-color:lightgrey;border-bottom:1px solid grey; }

.modalTitle { font-size:15pt;padding-left:20; } 

div.modal-content table  { clear:both;padding-top: 15; }
div.modal-content table th {} 
div.modal-content table td { padding:5; }
div.modal-content tr:nth-child(n) td {}

/* The Modal (background) */
.fade-bg {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content/Box */
.modal-content {
  background-color: #fefefe;
  margin: 5% 15%; /* 15% from the top, to center use auto */
  padding: 20px;
  border: 1px solid #888;
  width: 470px; /* Could be more or less, depending on screen size */
}
/* The Close Button */
.close {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}
.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

/* Loader */
.loader {
  border: 16px solid #f3f3f3; /* Light grey */
  border-top: 16px solid #3498db; /* Blue */
  border-radius: 50%;
  width: 80px; height: 80px;
  animation: spin 2s linear infinite;
  margin: 180 480;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
//Loader 소스 출처. w3schools https://www.w3schools.com/howto/howto_css_loader.asp
</style>
<script type="text/javascript">
try {
//----- 서비스 처리 로직 ---------------------------------------------------------------------------------------------------------
window.onload = function() {
console.log("onload-start:"+ (performance.now()/1000).toFixed(5) +" sec"); //수행시간 측정 시작

hrd.loadDataToTable();

console.log("onload-ended:"+ (performance.now()/1000).toFixed(5) +" sec"); //수행시간 측정 종료 
}
//employees.length = [];
var table = document.getElementsByTagName("table")[0];
var buttons = document.getElementsByTagName("button");
var hrd = {

//----- 추가된 기능 --------------------------------------------------------------------------------------------------------------

	loadDataToTable: function() {
		table.getElementsByTagName("tbody")[0].innerHTML="";
		if(employees.length>0) {
		  for(var i=0; i<employees.length; i++) { 
		  	var row = "<tr>"
		  			  +"<td>"+"<input type='checkbox' value='"+employees[i].employee_id+"'>"+"</td>"
					  +"<td>"+employees[i].employee_id+"</td>"
					  +"<td>"+getMaskedName(employees[i].name)+"</td>"
					  +"<td>"+employees[i].position+"</td>"
					  +"<td>"+employees[i].working_months+"</td>"
					  +"<td>"+employees[i].salary+"</td>"
			var hastask = false;
		  	for(var j=0; j<tasks.length; j++) { 
			  if (employees[i].employee_id == tasks[j].person_in_charge) {
			  	hastask = true;
				row+= "<td>"+tasks[j].task+"</td>"
					 +"<td>"+tasks[j].working_hours+"</td>"; 
			  } 
			}
			if(!hastask) row+= "<td>"+"업무 미배정"+"</td>"+"<td>"+"</td>";
			row+= "<tr>";
		    table.getElementsByTagName("tbody")[0].innerHTML+=row;
		  }
		} else { 
			table.getElementsByTagName("tbody")[0].innerHTML+= 
				"<tr><td colspan='8'>등록된 직원이 없습니다</td></tr>";
			for(var i=0; i<buttons.length ; i++) { 
				buttons[i].disabled = true;
			}
			
		}
	},

	verifyEmployeeInfoById: function(emp_id){ 
		var targetEmployee = this.getEmployeeInfoById(emp_id);
		if(targetEmployee=="not registered") {
			return "등록되지 않은 사번입니다";
		} 
		else {
			return targetEmployee;
		}
	},

	showEmployeeInfoModal: function(command){
		var modalBtn = document.getElementById("modalBtn");
		if(command == "add") {
			showModal("Y");
			modalTitle1.innerHTML = "<b>사원정보 등록</b>";
			modalBtn.innerHTML = "등록";
			modalBtn.onclick = function() { hrd.addEmployeeInfo(''); };
		}
		else if(command == "modify") {
			alert("수정 기능 준비중입니다");

			//showModal("Y");
			//modalTitle1.innerHTML = "<b>사원정보 수정</b>";
			//modalBtn.innerHTML = "수정";
			//modalBtn.onclick = function() { hrd.modifyEmployeeInfoById(''); };
		}
	},

	addEmployeeInfo: function(emp_id){
		var employee = this.setValidEmployee();
		if(employee == "notValid") return;
		console.log("hrd.addEmployeeInfo-start:" + (performance.now()/1000).toFixed(2) +" sec"); //수행시간 측정 시작

		var thisclass = this;
		showLoader("Y");
		setTimeout(function() {
		  employees.push(employee);
		  thisclass.loadDataToTable();
		  showLoader("N");
		  thisclass.afterTask("사원정보를 성공적으로 등록하였습니다");

		console.log("hrd.addEmployeeInfo-ended:"+ (performance.now()/1000).toFixed(2) +" sec"); //수행시간 측정 종료
		}, 1200);
	},

	//숙제. 수정 기능을 구현해보세요
	modifyEmployeeInfoById: function(emp_id){
		alert("수정 기능 준비중입니다");
		this.afterTask("사원정보를 성공적으로 수정하였습니다");
	},

	setValidEmployee() {
		var employee_id = document.querySelector("input[name=employee_id]");
		var name = document.querySelector("input[name=name]");
		var position = document.querySelector("input[name=position]");
		var working_months = document.querySelector("input[name=working_months]");
		var salary = document.querySelector("input[name=salary]");

		//serialize 
		var form = document.getElementsByTagName("form")[0]; var data = new FormData(form); 
		var dataObj = {}; for(let entry of data){ dataObj[entry[0]]=entry[1]; } var jsonData = JSON.stringify(dataObj); var jsonObj = JSON.parse(jsonData);
		var params = new URLSearchParams(data).toString();
		//var req = new XMLHttpRequest(); req.send(data); //send post request

		var employee_id_val = employee_id.value; //jsonObj.employee_id; 
		var name_val = name.value; 
		var position_val = position.value; 
		var working_months_val = working_months.value; 
		var salary_val = salary.value; 

		var regex = /^[ㄱ-ㅎ|가-힣|a-z|A-Z|]+$/;
		if(employee_id_val=="") { 
			alert("사원번호를 입력하지 않았습니다"); employee_id.focus(); return "notValid"; }
		else if(isNaN(employee_id_val)) { 
			alert("사원번호는 숫자만 사용가능합니다"); employee_id.focus(); return "notValid"; }

		if(name_val=="") { 
			alert("이름을 입력하지 않았습니다"); name.focus(); return "notValid"; }
		else if(!regex.test(name_val)) { 
			alert("이름은 문자만 사용가능합니다"); name.focus(); return "notValid"; }

		if(position_val=="") { 
			alert("직급을 입력하지 않았습니다"); position.focus(); return "notValid"; }
		else if(!regex.test(position_val)) { 
			alert("직급은 문자만 사용가능합니다"); position.focus(); return "notValid"; }

		if(working_months_val=="") { 
			alert("근무개월수를 입력하지 않았습니다"); working_months.focus(); return "notValid"; }
		else if(isNaN(working_months_val)) { 
			alert("근무개월수는 숫자만 입력 가능합니다"); working_months.focus(); return "notValid"; }

		if(salary_val=="") { 
			alert("연봉을 입력하지 않았습니다"); salary.focus(); return "notValid"; }
		else if(isNaN(salary_val)) { 
			alert("연봉은 숫자만 입력 가능합니다"); salary.focus(); return "notValid"; }

		var employee = new Object();
		employee.employee_id = employee_id_val;
		employee.name = name_val;
		employee.position = position_val;
		employee.working_months = working_months_val;
		employee.salary = salary_val;

		return employee;
	},

	afterTask(message) {
		alert(message);
		showModal("N");
	},

	deleteEmployeeInfoById: function(emp_id){
		var target = "";
		if(emp_id==null) target = document.querySelectorAll('input[type="checkbox"]:checked');
		else target = emp_id;
		for(var i=0; i<target.length; i++) {
		  	var verifyResult = ( emp_id==null ? this.verifyEmployeeInfoById(target[i].value) : this.verifyEmployeeInfoById(target) );
			if(verifyResult != "등록되지 않은 사번입니다") {
				var confimResult = prompt("사원 '"+getMaskedName(verifyResult)+"을 삭제하시겠습니까? 예 / 아니오");
				if(confimResult == "예") {
					var targetIndex = employees.map((emp) => emp.name).indexOf(verifyResult);
					employees.splice(targetIndex, 1);
					this.loadDataToTable();
				}
			}
			else {
				alert(verifyResult);
			}
		}
	},

//----- 기존의 기능 --------------------------------------------------------------------------------------------------------------

	getEmployeeInfoById: function(emp_id){ //사번으로 직원정보를 조회하는 함수

		for(var i=0; i<employees.length; i++) {
			if (employees[i].employee_id == emp_id) {
				return employees[i].name;
				/*(
					"사번:"+employees[i].employee_id+", "+
					"이름:"+employees[i].name+", "+
					"직위:"+employees[i].position+", "+
					"근무개월수:"+employees[i].working_months+", "+
					"연봉:"+employees[i].salary
				);*/
			}	
		}
		return "not registered"; 
	},
	getEmployeesTaskById: function(emp_id){ //사번으로 직원 담당업무를 조회하는 함수

		for(var i=0; i<tasks.length; i++) {
			if (tasks[i].person_in_charge == emp_id) {
				return (
					"업무코드:"+tasks[i].task_id+", "+
					"업무명:"+tasks[i].task+", "+
					"공수:"+tasks[i].working_hours+", "+
					"담당직원:"+tasks[i].person_in_charge
				);
			}	
		}
		return "할당된 업무가 없습니다"; 
	}
};
function getMaskedName(target_name) {
	return target_name.replace(/(?<=.{1})./gi, "*");
}

//Modal
var modal = document.getElementById("myModal"); // Get the modal
var span = document.getElementsByClassName("close")[0]; // Get the <span> element that closes the modal
function showModal(toggle_YN) {
	if(toggle_YN == "Y") modal.style.display = "block"; // When the user clicks on the button, open the modal
	else if(toggle_YN == "N")  modal.style.display = "none"; // When the user clicks on <span> (x), close the modal
}
span.onclick = function () { showModal("N"); };
window.onclick = function(event) { // When the user clicks anywhere outside of the modal, close it
  if (event.target == modal) modal.style.display = "none";
}
//모달 소스 출처. w3schools https://www.w3schools.com/howto/howto_css_modals.asp

//Loader 
var loader = document.getElementById("myLoader"); 
function showLoader(toggle_YN) {
	if(toggle_YN == "Y") loader.style.display = "block"; 
	else if(toggle_YN == "N")  loader.style.display = "none"; 
}

//document.getElementById("b").value;
} catch(e) { console.dir(e); }
</script>

 

여기까지 작성/저장하시고 .html 파일을 열면, 다음과 같은 화면이 출력될 것입니다.

 

메모장 대신에 html 소스를 출력해주는 사이트를 이용할 수도 있습니다.

대표적으로

w3schools( https://www.w3schools.com/html/tryit.asp?filename=tryhtml_default ) 이나

jsbin( https://jsbin.com/?html,output )이 있겠습니다. 

w3schools                                                                                                                           jsbin

 

참고로 정보 수정 기능은 여러분의 몫으로 남겨두어 구현하지 않았습니다.

 

혹시 여러분은 프로그래밍 하고 싶은 것이 있나요?

사실 개발은 이런 기대들로부터 시작된답니다. 

 

프로그램 맛보기 시리즈가 여러분께 도움이 되셨기를 바랍니다.

좋은 하루되세요.

 

-----------------------------------------------------------------------------------------------------------------------------------

추가로 data 부분을 다른 파일로 나눠 사용할 수도 있습니다.

data 부분을 data.js로 옮기고, 개발연습.html 에는 아래 외부스크립트를 추가하는 링크를 연결하여 적용이 가능합니다.

 

개발연습.html

<script type="text/javascript" src="data.js"></script>

data.js

//----- 서비스에 사용될 Data들 ---------------------------------------------------------------------------------------------------
var employees = [ //시스템에 등록된 사원목록
	{ 
		employee_id: '12345',
		name: '소영희', 
		position: '대리', 
		working_months: 36,
		salary: 4000
	}, 
	{ 
		employee_id: '67890',
		name: '장경식', 
		position: '과장', 
		working_months: 52,
		salary: 5300
	}, 
	{ 
		employee_id: '14703',
		name: '정명준', 
		position: '차장', 
		working_months: 85,
		salary: 6000
	}
];
var tasks = [ //시스템에 등재된 업무목록
	{  
		task_id: '54321',
		task: 'xx프로젝트 팀원',
		working_hours: 42,
		person_in_charge: '67890'
	}, 
	{  
		task_id: '09876',
		task: 'xx프로젝트 영업지원',
		working_hours: 57,
		person_in_charge: '12345'
	}
];

 

'웹개발 > 개론' 카테고리의 다른 글

웹개발자 필수 소양  (0) 2022.02.24
프로그래밍 맛보기(2부)  (0) 2021.07.15
프로그래밍 맛보기(1부)  (1) 2021.07.13
JAVA 기본 문법  (0) 2019.12.21
개발(프로그래밍)에 대하여  (0) 2019.12.21

안녕하세요 프로그래밍 맛보기 1부에 이어서,

프로그래밍 맛보기 2부를 작성하게 되었습니다.

2부에서는 1부에서 다루지 못했던 '객체'를 다루려고 합니다.

 

일반적으로 객체는 우리가 특정된 무엇을 설명하려고 할때의 대상을 가르켜 말합니다.

프로그래밍에서의 객체 또한 프로그래밍의 세계에서 특정 무엇을 설명/정의해놓은 로직이라 말할 수 있습니다. 

그것이 어떻게 설명되어지는지 한번 알아보도록 하겠습니다.

 

메모장을 열어 새로운 (.html)파일을 생성해보겠습니다.

다음과 같이 작성하고 저장하겠습니다.

이 또한 .html 파일을 열어보시면 다음과 같은 결과를 얻으실 수 있습니다.

이것이 우리가 프로그래밍 세계에서 정의한 객체입니다.

어리둥절하시죠? 우리가 정의한 객체에 대해 좀 더 파혜쳐보도록 하겠습니다.

그렇습니다 프로그래밍 세계에서는 이렇게 객체를 정의해두고, 객체로 업무를 수행하게 됩니다.

익숙해지기 위해 좀 더 작성해보도록 하겠습니다.

//2. 내장된 객체 이용하기 
var map1 = new Map();
map1.set('key', 'value');
alert(map1.get('key'));

 이와 같이 수행을 하니 다음과 같은 결과가 출력됩니다.

헌데 Map이라는 객체는 우리가 정의하지 않았음에도 사용이 가능하다는걸 알 수 있습니다.

Map이라는 객체는 자바스크립트 코드를 실행하는 (브라우저)엔진에 내장(정의)되어있기 때문입니다.

우리가 이 객체를 볼수가 없는데 어떻게 사용할 수 있을까요?

이런 내장된 함수들의 스펙은 많은 레퍼런스/도큐먼트에서 제공되어지고 있으므로

구글링 조금만 해보시면 금방 찾으실 수 있습니다.

아래는 제가 찾은 Map객체에 대해 설명해놓은 레퍼런스 입니다. 참고해보시죠.

https://www.w3schools.com/Js/js_object_maps.asp

 

다음 코드를 작성해보시면서 좀 더 알아보도록 하시겠습니다.

<script>

//1. 객체 생성 및 제어
class UserObject {
  constructor(parameter1, parameter2) {
    this.name = parameter1;
    this.age = parameter2;
  }

  // Getter
  get userInfo() {
    return "이름:" + this.name + ", 나이:" + this.age;
  }

  changeInfo(changed_name, changed_age) {
  	this.name = changed_name;
    this.age = changed_age;
  }
}

var userObject = new UserObject("강용구", "38");
alert(userObject.userInfo);

userObject.changeInfo("고진석", "27");
alert(userObject.userInfo);

//2. 내장된 객체 이용하기 
var map1 = new Map();
map1.set('key', 'value');
alert(map1.get('key'));

//-------------------------------------------------> 여기까지 진행했습니다~

//3. 다른 방식으로 객체 다뤄보기 
//3_1. 생성자 방식 
var order = new Object(); 
order.product_name = '생강차'; 
order['cost'] = 3500; 
order.receipt = function(){ 
	return `주문하신 ${this.product_name}, ${this.cost}원 입니다.`; 
};
alert(order.receipt());

//3_2. 리터럴 방식 
var book = { 
	name: '바람의검귀(하)', 
	genre: '무협', 
	bookInfo: function(){ 
		return `책명: ${this.name}, 장르: ${this.genre}`; 
	} 
};
alert(book.bookInfo());

//4. 숙제. 본인만의 객체를 만들어보세요

//5. 따라하기. 다음 인사관리 업무모델을 따라 만들어보세요
var employees = [ //시스템에 등록된 사원목록
	{ 
		employee_id: '12345',
		name: '소영희', 
		position: '대리', 
		working_months: 36,
		salary: 4000
	}, 
	{ 
		employee_id: '67890',
		name: '장경식', 
		position: '과장', 
		working_months: 52,
		salary: 5300
	}
]
var tasks = [ //시스템에 등재된 업무목록
	{  
		task_id: '54321',
		task: 'xx프로젝트 팀원',
		working_hours: 42,
		person_in_charge: '12345'
	}, 
	{  
		task_id: '09876',
		task: 'xx프로젝트 영업지원',
		working_hours: 57,
		person_in_charge: '12345'
	}
]
var hrd = {
	getEmployeeInfoById: function(emp_id){ //사번으로 직원정보를 조회하는 함수

		for(var i=0; i<employees.length; i++) {
			if (employees[i].employee_id == emp_id) {
				return (
					"사번:"+employees[i].employee_id+", "+
					"이름:"+employees[i].name+", "+
					"직위:"+employees[i].position+", "+
					"근무개월수:"+employees[i].working_months+", "+
					"연봉:"+employees[i].salary
				);
			}	
		}
		return "사원정보가 없습니다"; 
	},
	getEmployeesTaskById: function(emp_id){ //사번으로 직원 담당업무를 조회하는 함수

		for(var i=0; i<tasks.length; i++) {
			if (tasks[i].person_in_charge == emp_id) {
				return (
					"업무코드:"+tasks[i].task_id+", "+
					"업무명:"+tasks[i].task+", "+
					"공수:"+tasks[i].working_hours+", "+
					"담당직원:"+tasks[i].person_in_charge
				);
			}	
		}
		return "할당된 업무가 없습니다"; 
	}
}
alert(hrd.getEmployeeInfoById('67890')); //사번이 67890인 직원 정보 조회
alert(hrd.getEmployeesTaskById('12345')); //사번이 67890인 직원 담당업무 조회

</script>

5. 업무(비즈니스)모델 출력 결과

 

코딩을 하시면서 프로그래밍에 대해 흥미가 생기시나요?

앞에서 말씀드린 기본 개념을 숙지하시면서 

레퍼런스들을 구글링/서적으로 찾아가시면서

내가 구상하는 서비스를 충분히 제작하실 수도 있습니다.

 

물론 그러시려면 열정이라는 무기를 쥐고 부단하게 노력하는 자세로,

목표에 한발한발 내딛여가는 여정을 시작하셔야 겠습니다. 

 

좋은 하루되세요

 

 

'웹개발 > 개론' 카테고리의 다른 글

웹개발자 필수 소양  (0) 2022.02.24
프로그래밍 맛보기(3부)  (0) 2021.07.18
프로그래밍 맛보기(1부)  (1) 2021.07.13
JAVA 기본 문법  (0) 2019.12.21
개발(프로그래밍)에 대하여  (0) 2019.12.21

안녕하세요. 프로그래밍에 관심이 있지만 처음 접하시는 분들이

프로그래밍을 쉽고, 간단하고, 재밌게 맛보도록 해드리기 위해 다음 글을 쓰게 되었습니다. 

한번 경험해보시겠다면 저를 따라오시죠~

 

우선 프로그래밍을 시작하기에 앞서

개발환경, 개발툴을 갖추어야 하는데요.

저는 자바스크립트/HTML 로만 진행할것 이어서

컴퓨터에 notepad.exe(메모장) 만 깔려있으셔도 따라오실 수 있습니다. 

물론 VSCode나 SublimeText와 같은 텍스트 에디터가 있다면

더욱 편리하게, 또 격식있게 프로그래밍을 경험하실 수 있겠지만요

 

우선 처음이시니 작업표시줄의 시작, 돗보기 옆에 note...를 치시고 

메모장을 열어보겠습니다.

 

 

그리고 다음을 작성하겠습니다

아자! 개발 입문하기!
<script>

	//1. 변수와 연산
	var x = 1; //* 숫자 형(타입)
	var y = 2;
	alert(x+y);

</script>

작성 후에 저장을 하시는데요, 파일 형식을 모든 파일로 바꾸시고

아래와 같이 '파일명.html' 형태로 확장자를 .html로 저장하시는 것이 포인트가 되겠습니다!

 

여기까지 하셨다면 아래와 같이 파일이 생성 되셨을텐데요. 이것을 실행해봅시다.

 

그러면 이렇게 뚝허니 '3' 이라고 알람창이 등장합니다.

그렇습니다. 우리는 위의 코드를 작성하여 아래의 동작을 실행시킨 것입니다.

보아하니 3은 위의 코드에서 x=1, y=2 로 선언했던 값들을 x+y로 연산하였기 때문에

나타난 것임을 알 수 있습니다. 

뭐가 뭔지 아직 잘 모르시겠다구요? 

네 그럴 수 있습니다. 처음해본다면 모든게 낮설 수 밖에 없습니다.

그럼 조금 더 작성(프로그래밍)해보겠습니다.

아자! 개발 입문하기!
<script>

	//1. 변수와 연산
	var x = 1; //* 숫자 형(타입)
	var y = 2;
	alert(x+y);

	//2. 함수와 함수호출
	function fx() {
		alert("나는 함수라네~"); //* 문자 형(타입)
	}
	fx();

	//3. 서랍장 같은, 배열 
	var array = [10, 20, 30, 40];
	alert("3번. "+ array[0]);

</script>

그리고 해당 파일을 다시 실행해보면... 다음과 같은 결과를 확인하실 수 있습니다.

조금 적응되시는지요? 

그렇습니다 익숙해지시려면 지금까지 했던것 처럼 적응될때까지 계속 쓰고/보고 또 쓰고/보고 해야할 것입니다.

작성 안을 드릴테니 더 많은 코드를 작성해보고 경험해보시겠습니다.

아자! 개발 입문하기!
<script>
	//* 다음을 따라 적고, 그리고 브라우저 화면에서 확인해보세요~
	//  이것이 공부입니다. alert()의 주석을 풀어 결과물을 확인해보세요~

	//1. 변수와 연산
	var x = 1; //* 숫자 형(타입)
	var y = 2;
	//alert(x+y);

	//2. 함수와 함수호출
	function fx() {
		//alert("나는 함수라네~"); //* 문자 형(타입)
	}
	fx();

	//3. 서랍장 같은, 배열 
	var array = [10, 20, 30, 40];
	//alert("3번. "+ array[0]);

	//4. 반복 업무, 반복문(for, while) 
	//4_1.
	for(var i=1; i<=3; i++) { //세번 반복 
		//alert("4_1번. "+ array[i]);
	}
	//4_2.
	var j=1;
	while(j<=3) { //세번 반복 
		//alert("4_2번. "+ array[j]);
		j++;
	}

	//5. 만약에 이럴때, 조건문(if, switch-case) 
	//5_1.
	var jogun1 = 2 < 3; //2가 3보다 작다. '참' 으로 다음을 실행
	var jogun2 = true || false; //true || false 는 true를 반환 함으로 다음을 실행 
	if(jogun1) { 
		//alert("5_1번. "+ jogun1); //* true, false 는 논리 형(타입)이라고 부릅니다
	}
	//5_2.
	var jogun3 = "bannana";
	switch(jogun3) { //true || false 는 true를 반환 함으로 다음을 실행 
		
		//case "apple": alert("5_2번. 사과"+ jogun3); break;
		//case "bannana": alert("5_2번. 바나나"+ jogun3); break;
		//default: alert("5_2번. 종료"+ jogun3); break;
	}

	//6. 숙제. 반복문을 이용하여 구구단 9단까지 찍어보세요 
	//alert("구구단 시작!");
	var dan = 2; //힌트로, 이는 2단 예시입니다!
	for(var i=1; i<=9; i++) {
		//alert(dan + "x" + i + "=" + i*dan);
	}

</script>


<!-- 7. 따라해보기. 같이 따라해보아요~
	    흰벽에 내가 좋아하는 액자(.img)를 걸어보기 -->

<!-- 벽 입니다 -->
<div id="wall" style="padding:10">
	나는 벽. 여기에다 액자를 걸어두지..<br><br><br><br>
</div>

<script>

	//액자1 
	document.getElementById("wall").innerHTML += "<div class='frame'><img src='https://source.unsplash.com/random/500x500?sig=1' style='height:250;'></div>";

	//액자2
	document.getElementById("wall").innerHTML += "<div class='frame'><img src='https://picsum.photos/500/500' style='height:250;'></div>";

	//액자3
	document.getElementById("wall").innerHTML += "<div class='frame'><img src='https://random.imagecdn.app/500/500' style='height:250;'></div>";

</script>

<!-- 아래는 이미지를 액자스럽게 꾸며주는 스타일 소스 입니다. 작성하셔도 되고, 안하셔도 됩니다 -->
<style>
img {
  border:solid 2px;
  border-bottom-color:#ffe;
  border-left-color:#eed;
  border-right-color:#eed;
  border-top-color:#ccb;
  max-height:100%;
  max-width:100%;
}

.frame {
  background-color:#ddc;
  border:solid 5vmin #eee;
  border-bottom-color:#fff;
  border-left-color:#eee;
  border-radius:2px;
  border-right-color:#eee;
  border-top-color:#ddd;
  box-shadow:0 0 5px 0 rgba(0,0,0,.25) inset, 0 5px 10px 5px rgba(0,0,0,.25);
  box-sizing:border-box;
  display:inline-block;
  margin:5vh 5vw;
  height:40vh;
  padding:4vmin;
  position:relative;
  text-align:center;
  &:before {
    border-radius:2px;
    bottom:-2vmin;
    box-shadow:0 2px 5px 0 rgba(0,0,0,.25) inset;
    content:"";
    left:-2vmin;
    position:absolute;
    right:-2vmin;
    top:-2vmin;
  }
  &:after {
    border-radius:2px;
    bottom:-2.5vmin;
    box-shadow: 0 2px 5px 0 rgba(0,0,0,.25);
    content:"";
    left:-2.5vmin;
    position:absolute;
    right:-2.5vmin;
    top:-2.5vmin;
  }
}
//액자스타일 출처: https://codepen.io/chris22smith/pen/PbBwjp

//9. 숙제. 구글링을 하셔서 이(액자 스타일)와 같은 꿀템 소스들을 잘활용해보세요~
</style>

 

여기까지 잘 따라오셨다면~ 이 처럼 프로그래밍으로 벽에다 액자도 걸어보셨을 겁니다. 

<script>
//+ 액자4
document.getElementById("wall").innerHTML += "<div class='frame'><img src='"
	+"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE1kG7%2Fbtq9qjY5W5x%2F7kXkyzYFZVD54qnMLGf2E1%2Fimg.png"
+"' style='height:250;'></div>";
</script>

 

고생하셨습니다~ 즐거운 하루되세요

'웹개발 > 개론' 카테고리의 다른 글

프로그래밍 맛보기(3부)  (0) 2021.07.18
프로그래밍 맛보기(2부)  (0) 2021.07.15
JAVA 기본 문법  (0) 2019.12.21
개발(프로그래밍)에 대하여  (0) 2019.12.21
server-side 언어중 JAVA에 대하여  (0) 2019.12.21

안녕하세요 이번에는 구름IDE에서 개발하고 개발한 사이트를 띄워보려고 합니다.

 

먼저 구름IDE 계정을 생성한 후 대시보드를 열어보겠습니다.

대시보드는 컨테이너 단위로 개발환경 및 프로젝트를 생성하고 관리하는 인터페이스 입니다.

여기서 새 컨테이너를 클릭하여 개발작업을 수행할 개발환경을 구성하는 화면으로 이동하겠습니다

 

 

컨테이너 생성에서 프로젝트의 이름 등의 프로젝트 정보와 

배포방식, 소프트웨어 스택, OS 버전개발환경의 스펙을 선택하여 프로젝트를 생성할 수 있습니다.

간단한 사이트는 php로 제작하기 좋습니다. phpMyAdmin이라는 브라우저에서 실행하는 DB툴 또한 지원하고 있습니다.

저의 경우 웹컴포넌트를 수월하게 모듈화할 수 있는 vue.js(with node.js)로 개발하겠습니다. 

데이터베이스도 필요하다면 추가 모듈/패키지에서 해당 DB를 선택하여 생성을 눌러줍니다.

* 구름IDE에서 DB사용 방법은 구글님과 help.goorm, blog.goorm 을 통해서 찾으실 수 있습니다.

 

 

 

* 참고로 서비스 배포까지 생각하신다면 공개범위 Private, 배포할 플랫폼을 선택하셔야합니다.

  HerokuCLI 설치API-KEY 취득 방법은 아래 사이트를 참고하시면 되겠습니다

① HerokuCLI 설치 devcenter.heroku.com/articles/getting-started-with-nodejs

② Heroku API-KEY 취득 devcenter.heroku.com/articles/authentication#api-token-storage

 

그럼 아래와 같이 프로젝트 컨테이너가 생성이 되며

실행을 누르면 프로젝트를 IDE로 접근할 수 있게 됩니다.

 

 

이 스타터팩이 어떻게 구성되어있는지 프로젝트를 한번 구동하여 확인해보겠습니다.

'프로젝트 > 실행 > 실행 명령 추가' 에서 프로젝트를 구동시키는 스크립트를 작성하고 '실행'을 클릭합니다.

* IDE에서 각 소프트웨어 스택에 따라 default 스크립트를 제공하고 있으니 스크립트 작성 없이 실행하실 수 있습니다

  다만, node 관련 스택의 경우 default 스크립트에 npm install이 포함되어 있으므로 두번째 실행부터는 install를 빼고

  npm run serve 만 입력하여 실행하시는 것이 구동속도가 빠릅니다.  

 

 

구동하셨다면 잘컴파일 되었는지 콘솔을 한번 확인해보신 후 

미리보기를 클릭하시면

프로젝트의 결과물을 확인하실 수 있습니다.

 

 

커스터마이징을

좀 해보겠습니다.

이렇게 '구름IDE로 간편하게 웹개발하기' 는 성공적

 

 

이었습니다. 

 

참편리합니다.

우린 클라우드 서비스를 통해 쉽게 개발하고 배포할 수 있는 좋은 시대에 살고있습니다.

여러분도 한번 도전해보시는건 어떻신가요.

 

다음에 다른 컨텐츠로 찾아뵐 수 있도록 하겠습니다.

코로나 조심하시고 건강하세요.

 

즐코!

 

Go는 구글에서 개발한 High-Level 프로그래밍 언어로

문법이 단순하며, 컴파일이 빨라 뛰어난 퍼포먼스를 자랑하는 차세대 언어입니다

저장소 로직를 작성하는데 많이 사용하며 구글, 드롭박스, 넷플릭스 등 클라우드 서비스에 많이 사용됩니다

 

https://golang.org/ 에서 연습해볼 수 있습니다

http://bitly.kr/MpsTmsV8Q 에서

Go에 대한 더 많은 정보를 얻으실 수 있습니다

 

사실 Go 기반의 웹프레임워크도 있습니다

그래서 이번 시간에는

 

Revel 웹프레임워크를 사용해보겠습니다

아래 사이트를 참고하여 개발환경을 구성하시면 되겠습니다

https://revel.github.io/tutorial/gettingstarted.html

 

환경 구성 첫단계는 역시나

Go를 설치하고

환경 변수를 추가하는 것 입니다 

 

다음 사이트에서 Go를 다운 받고

https://golang.org/dl/

설치 후 환경변수를 추가합니다

 

Go가 잘 설치되었는지 확인해보겠습니다

cmd 창을 여셔서 go version을 입력합니다

설치 및 환경변수 추가가 성공적이었다면 Go의 버전이 다음과 같이 출력됩니다

그럼 go run 파일명.go 를 통해

소스를 컴파일 및 실행 할 수 있습니다 

 

이어서 Revel 웹프레임워크를 설치하겠습니다

Git을 사용해야하므로, 먼저 다음 사이트에서 Git을 다운받고 설치하겠습니다

https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

 

여기까지 완료되셨다면 

go get github.com/revel/revel 를 입력하여

Revel을 설치합니다

 

Revel이 설치되었다면 

Revel 프로젝트를 생성할 수 있습니다

이는 GOPATH 디렉토리 내에서 

revel new 프로젝트명 을 입력하여 생성 할 수 있고

revel run 프로젝트명 을 입력하여 서버를 띄울 수 있습니다

 

GOPATH src에서

프로젝트가 생성된 걸 확인 할 수 있습니다  

 

코틀린 개발을

인텔리제이IDE 개발환경에서 진행하겠습니다

다운 및 설치를 진행합니다

 

https://www.jetbrains.com/ko-kr/idea/download/#section=windows

 

인텔리제이가 준비 되었다면 코틀린 프로젝트를 생성해보겠습니다

 

우선 일반 SE kotlin 프로젝트를 생성해보겠습니다

 

이번엔

Spring Boot gradle kotlin 프로젝트를 만들어보겠습니다

인텔리제이에 Spring Boot plugin을 설치하면

새 프로젝트 > spring initializr 에서 Spring Boot 프로젝트를 생성할 수 있지만 

 

Community 버전에서는 이를 지원하지 않는답니다

 

이에 대한 대안으로 

https://start.spring.io/ 에서 Spring Boot gradle kotlin 기본 프로젝트를 생성할 수 있습니다

프로젝트 정보를 작성 후 Generate를 누르면

프로젝트를 담은 .zip파일을 내려 받게됩니다

 

프로젝트의 압축을 풀고 C:\에 배치하겠습니다

이전에 생성했던 프로젝트의 이름과 중복되므로 

기존 프로젝트를 Close Project하고 삭제하겠습니다

 

Import Project로 프로젝트을 열겠습니다

 

다음 소스를 추가하고 서버를 실행하면

화면이 다음과 같이 정상적으로 출력될 것입니다 

 

STS에서 사용한다고 하면

Kotlin 플러그인을 설치하고, Kotlin 프로젝트를 생성하면 됩니다

 

SE 프로젝트 테스트로 잘 작동하는 것을 확인할 수 있습니다

 

ASP .NET 개발을 위해 개발 환경을 구성해보겠습니다

먼저 ASP .NET 개발용 툴 Visual Studio를 설치해야합니다 

다음 마소 documentation을 참고하셔서 설치를 진행하시면 되겠습니다

 

https://docs.microsoft.com/ko-kr/visualstudio/ide/quickstart-aspnet-core?view=vs-2019

 

Visual Studio를 설치하셨다면 

ASP .NET 프로젝트를 생성해보겠습니다

 

다음과 같이 ASP 검색이 안된다면

Visual Studio Installer를 통해 추가 항목을 설치해주시면 됩니다

 

잘 설치하셨다면 ASP 검색 시

다음 결과들을 확인 할 수 있습니다

ASP .NET Core 웹 응용 프로그램을 생성하겠습니다

 

생성할 프로젝트의 정보를 입력합니다

 

ASP .NET Core MVC 웹 애플리케이션을 선택합니다

 

기본 구성으로 프로젝트를 생성하셨다면

바로 빌드하여 기본 구성의 로컬 서버를 띄울 수 있습니다 

참 쉽죠? 

 

추가적인 화면은 Razor 뷰를 생성하고

Controller에 View를 등록함으로서 생성 가능합니다

 

서버/경로/파일명 형태의 url로 접근이 가능합니다

 

Visual Studio의 장점이라고 한다면

친밀한 User Interface도 있지만 

일부 프로젝트에 대하여

강력한 UI Editor를 제공하기도 합니다

 

이번에는

UI Editor를 지원하는 

.NET Web Forms 프로젝트를 생성해보겠습니다

 

새 프로젝트 만들기에서

ASP .NET Framework 프로그램을 선택합니다

 

이전에 만들었던 프로젝트와 상위 경로를 공유하겠습니다

 

Web Forms 프로젝트를 선택하고

우측 MVC 등 필요한 핵심 참조를 추가합니다

 

프로젝트 기본 구성에 Test라는 화면을 추가하고

로컬 서버를 띄웠습니다

 

.aspx 파일에서는 Web Forms에서 지원하는 UI 팔레트를 사용할 수 있습니다

Winform에 비해 컴포넌트나 속성에서 부족한 느낌을 받긴 했습니다

시작하기 앞서 파이썬이라는 언어에 대해 잠시 알아보겠습니다

위키에서 발췌한 내용이며

보시다 싶이 파이썬은 소스의 단순화에 중점을 둔 언어입니다

그래서 개발하시다보면 생략된 문법이 많아

여타 언어에 비해 소스가 간결하단 점을 느낄 수 있습니다 

 

파이썬 개발에 앞서 개발환경을 구성해보겠습니다

 

파이썬 인터프리터를 다운, 설치 합니다

https://www.python.org/

 

cmd에서 python을 사용하기 위하여

python에 대한 환경변수를 추가합니다

 

python -V를 통하여 다음과 같이 개발환경이 잘 구성되어 있는지 확인 할 수 있습니다

 

python -m venv env 를 통해 파이썬 프로젝트를 생성할 수 있습니다

 

{프로젝트 경로}\env\Scripts\activate.bat 를 실행하여

pip install django 를 통하여 django 프레임워크를 추가할 수 있습니다

 

개발용 툴은 VS Code를 사용하겠습니다

설치 및 다운 후 extentions에서 python plugin을 추가합니다

 

https://code.visualstudio.com/download

 

그리고 Command Palette에서 여러 설정을 합니다

 

1. 파이썬 인터프리터 등록

 

2. 터미널 등록

 

3. VS code에 터미널 인터페이스 추가

 

파이썬 코드를 작성하고 ▷를 눌러 소스를 빌드하여

개발 환경이 잘 구성되었는지 확인 할 수 있습니다

 

그리고 pylint를 설치하라는 팝업을 볼 수 있습니다

pylint 설치 후

이 처럼 VS Code Terminal에서 python CLI를 실행이 가능합니다

Extentions에서 

 

앞에서 cmd에서 pip install django 을 하였습니다

이어서 django 프로젝트를 생성하고 프로젝트를 로컬 서버에 띄워보겠습니다

 

다음 CLI를 입력합니다

C:\example4\env\Scripts\activate.exe

python

 

cd ..

django-admin startproject testPjt

 

cd testPjt

python manage.py runserver

 

여기까지 완료 하셨다면

127.0.0.1:8000 에서

다음과 같은 화면을 확인 할 수 있습니다

* 참고로 File > Open Folder를 통해 좌측 폴더 경로를 띄울 수 있습니다

 

프로젝트 커스터마이징을 진행하겠습니다

콘솔 또는 터미널에서 Ctrl + C를 눌러 로컬 서버를 종료하겠습니다

 

그리고 settings.json에서 python.pythonPath를

해당 프로젝트 경로의 python에 접근하도록 설정하겠습니다

 

그리고 다음 CLI를 입력하여

새 커스텀 App을 (두개) 생성하겠습니다

python manage.py startapp testApp

python manage.py startapp testApp1

 

생성된 소스들을 수정하겠습니다

 

testApp/views.py

1
2
3
4
5
6
7
from django.shortcuts import render
from django.http import HttpResponse
 
# Create your views here.
def test(request):
    
    return HttpResponse("hi test")
 

 

testApp1/views.py

1
2
3
4
5
6
7
from django.shortcuts import render
from django.http import HttpResponse
 
# Create your views here.
def index(request):
    
    return HttpResponse("hi test1")
 

 

urls.py

1
2
3
4
5
6
7
8
9
10
from django.contrib import admin
from django.urls import path
from testApp import views as testApp
from testApp1 import views as testApp1
 
urlpatterns = [
    path('admin/'admin.site.urls),
    path(''testApp1.index, name='index'),
    path('test'testApp.test, name='test')
]
 

 

그리고 다시 서버를 실행하면

다음과 같은 결과를 확인 할 수 있습니다

 

프로젝트 구축을 위한 환경설정을 하겠습니다

 

1. 서브라임 텍스트(text editor)가 준비되어야 하며

2. node.js를 설치해야 합니다

 

node.js 설치를 진행해보겠습니다

https://nodejs.org/ko/download/

 

node.js를 설치하셨다면

node CLI를 이용하여 리액트 프로젝트를 생성하겠습니다

 

우선 다음 npm CLI를 통하여 create-react-app를 설치해야합니다

npm install -g create-react-app

 

그리고 다음 CLI로 리액트 프로젝트를 생성합니다

create-react-app {생성할 프로젝트명} 

 

프로젝트가 준비되었다면 

프로젝트를 sublime에 연결하겠습니다

 

생성한 프로젝트가 이상이 없는지 확인해보겠습니다

cmd에서 다음 CLI를 입력합니다

cd {프로젝트 디렉토리\src} & npm start

 

그리고 브라우저에서 localhost:3000 로 이동하면

다음과 같이 리액트 프로젝트 초기화면을 확인 할 수 있습니다

 

프로젝트를 빌드하기 위해 매번 cmd를 사용해야하는 번거로움이 있습니다

편의를 위해 단축키 이용한 빌드를 구성해보겠습니다

 

우선 cmd창에서 다음 npm을 설치해줍니다     

npm install react-scripts@2.1.8 

 

이왕 npm을 설치하는 김에 다음 npm들도 설치해줍니다

npm i npm-cli --save

npm i jquery

 

 

이렇게 jquery를 설치하셨다면 

package.json에 dependencies에 jquery를 추가하고

jquery를 사용할 프로젝트에 $를 import하시면 되겠습니다

 

단축키로 빌드하기 위해선

scripts 부분은 다음과 같이 작성되어야 합니다 

 

후 단축키 빌드를 위한 sublime-build 파일을 작성해야합니다

다음과 같이 진행합니다

 

여기까지 되셨다면

간편하게 Ctrl+B 단축키로 이 처럼 브라우저를 띄울 수 있습니다

 

여기까지 되셨다면 다음과 같이 프로젝트를 구성해보겠습니다

 

위와 같이 dependency들을 추가해야합니다

이를 위하여 yarn CLI를 사용하도록 하겠습니다

 

npm i -g yarn

으로 yarn을 설치합니다

 

그리고 cmd에서 해당 프로젝트로 cd(change directory) 후

yarn add {추가할 패키지} 로 다음 dependency들을 추가합니다

ex) yarn add express

 

express

mongoose 
express-graphql 
graphql 
cors

 

본격적으로 프로젝트에 다음 소스들을 추가하겠습니다

 

layout.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import React from 'react';
import $ from 'jquery'
 
export class Head extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div id="head">
               
                <a href="#"><h2>TEST Site</h2></a>
                <button>메뉴6</button>
 
            </div>
        );
    }
}
 
export class Nav extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div id="nav">
 
                <ul className="hlist">
                    <li><a href="#" onClick={function(){window.location.href='/react';}}>list</a></li>
                    <li><a href="#">메뉴2</a></li>
                    <li><a href="#">메뉴3</a></li>
                    <li><a href="#">메뉴4</a></li>
                    <li><a href="#">메뉴5</a></li>
                </ul>
 
            </div>
        );
    }
}
 
export class Main extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div id="main">
                <div id="inner">
 
                </div>
            </div>
        );
    }
}
 
export class Foot extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div id="foot">
 
                <p>copyright by webman</p>
 
            </div>
        );
    }
}
 
 
export default class Layout extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div>
 
                <Head />
                <Nav />
                {this.props.list}
                <Foot />
 
            </div>
        );
    }
}
 
 

 

main.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import React from 'react';
import $ from 'jquery'
 
 
function Bar() {
    const style1 = {
            display:'inline-block',
            textAlign:'right',
            borderTopLeftRadius:5,
            paddingTop:3+'px',
            paddingLeft:5+'px',
            width:100+'%',
            backgroundColor:'black'
        };
    
        return (
            <div style={style1}>
 
    <span>hi</span>
    <ul className="hlist">
        <li><a href="#" onClick={function(){doAjax('c.do');}}>C</a></li>
        <li><a href="#" onClick={function(){doAjax('u.do');}}>U</a></li>
        <li><a href="#" onClick={function(){r();}}>R</a></li>
        <li><a href="#" onClick={function(){doAjax('d.do');}}>D</a></li>
    </ul>
 
            </div>
          );
}
 
function Input() {
        
        return (
            <div style={{float:'right',height:50+'%',width:46+'%',margin:1.8+'%'}}>
 
        <ul>
            <li>
                <label>id</label><input type="" name="id" placeholder="Id는 자동생성 됩니다" readOnly/>
            </li>
            <li>
                <label>firstName</label><input type="" name="firstName"/>
            </li>
            <li>
                <label>lastName</label><input type="" name="lastName"/>
            </li>
            <li>
                <label>ref</label><input type="" name=""/>
            </li>
        </ul>
 
            </div>
          );
}
 
function ListItems(list) {
    const array=[];
    $.each(list, function(idx, val) {
        const list=JSON.parse(val);
        for(var i=0; i<list.length; i++) {
            array.push(list[i]);
        }
    });
    console.log(array[0]);{/**/}
 
      return (
          <tbody>
 
        {array.map(function(value, index){
            return (
                <tr>
                    <td className="default" key={index}>{value.id}</td>
                    <td className="default" key={index}>{value.firstName}</td>
                    <td className="default" key={index}>{value.lastName}</td>
                    <td className="default" key={index}></td>
                </tr>
            );
        })}
 
        </tbody>
      );
}
 
function List({list}) {
 
        return (
            <div>
                
<form name="form" id="form" style={{margin:0+'px'}}>
 
<div style={{display:'inline-block',height:100+'%',width:100+'%'}}>
    <table cellPadding="0" cellSpacing="0">
        
        <tr>
            <th>id</th>
            <th>firstName</th>
            <th>lastName</th>
            <th>ref</th>
 
        </tr>
 
            {/*<ListItems data={list}/>*/}
        <tbody>
            <tr>
                <td className="default" ></td>
                <td className="default" >{list}</td>
                <td className="default" ></td>
                <td className="default" ></td>
            </tr>
            <tr>
                <td className="default" ></td>
                <td className="default" >{list}</td>
                <td className="default" ></td>
                <td className="default" ></td>
            </tr>
        </tbody>
        
    </table>
 
    <Input/>
 
</div>
 
</form>
 
            </div>
          );
}
 
export default class Main1 extends React.Component {
    constructor(props) {
        super(props);
    }
 
    Bar() {}
    Input() {}
    List(props) {}
 
    render() {
        return (
            <div id="main">
                <div id="inner">
            
                <Bar />
                <List list={this.props.list}/>
                {/*{this.props.list}*/}
                
                </div>
            </div>
        );
    }
}
 
function r() {
    $('input[name=id]').val($('.clicked:eq(0)').html());
    $('input[name=firstName]').val($('.clicked:eq(1)').html());
    $('input[name=lastName]').val($('.clicked:eq(2)').html());
}
 
function ajaxList() {
    $.ajax({ 
        url: "/list.do"// 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
        type: "GET"// HTTP 요청 메소드(GET, POST 등)  
        dataType: "json"// 서버에서 보내줄 데이터의 타입  
        
        success:function(returnMap){
            //console.log(returnMap.api);
            //console.log(returnMap.dbData);
            alert(returnMap.dbData);
            ListItems(returnMap.dbData);
        },
        
        error:function(jqXHR, status, errorThrown){
            //console.log(jqXHR.responseText);
        }
    });
}
 
function doAjax(url){
    var params = $("#form").serialize();
    $.ajax({ 
        url: "/"+url, // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
        data: params, // HTTP 요청과 함께 서버로 보낼 데이터 
        type: "POST"// HTTP 요청 메소드(GET, POST 등)  
        dataType: "json"// 서버에서 보내줄 데이터의 타입  
        
        success:function(returnMap){
            //console.log(returnMap.api);
            ajaxList();
        },
        
        error:function(jqXHR, status, errorThrown){
            //console.log(jqXHR.responseText);
            ajaxList();
        }
    });
}
 
$(document).ready(function() {
    //ajaxList();
 
    $("#main td").mouseover(function(e){
        $(e.target).parent().children('td').removeClass('default');
        $(e.target).parent().children('td').addClass('hover');
    });
    $("#main td").mouseout(function(e){
        $(e.target).parent().children('td').removeClass('hover');
        $(e.target).parent().children('td').addClass('default');
    });
 
    $(document).click(function(e) {
           if($(e.target).is("#main td")) {
        
            if($(e.target).hasClass('clicked')) {
                $('td').removeClass();
                $('td').addClass('default');
            
            } else {
                $('td').removeClass();
                $('td').addClass('default');
            
                $(e.target).parent().children('td').removeClass();
                $(e.target).parent().children('td').addClass('clicked');
            
                //$('input[name=id]').val($(e.target).siblings(':nth-child(1)').html());
                //$('input[name=firstName]').val($(e.target).siblings(':nth-child(2)').html());
                //$('input[name=lastName]').val($(e.target).siblings(':nth-child(3)').html());
            }
        } else {    
 
        }
    });
});    
 
 

 

main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#body {
    height:100%;
    margin:0;
}
 
span, label {
    display:inline-block;margin:6px 25px 8px 25px;
    font-size:18px;
}
 
input {
    padding:5px;margin-top:-15px
}
 
#head {
    width:100%;height:60px;
    background-color:white;
}
 
#head a {
    display: inline-block;
    float: left;margin-left:10%;
    text-decoration: none;color:black;
 
}
 
#head button {
    float: right;margin: 8px 10%;
    font-size: 20px;padding:8px 15px;
    background-color: transparent;
    border:0.1em solid #333;border-radius:8px;
}
 
#head button:hover {;
    background-color: #333;
    color:white;
}
 
#nav {
    width:100%;
    border-top:1px solid lightgrey;
    background-color: #333;
    text-align:center;
    margin-bottom: -5px;
}
 
ul {
  display:inline-block;
  list-style-type:none;
  margin:0;padding:0;overflow:hidden;
  background-color: transparent;
}
 
li {
  float: left;
}
 
li a {
  display: block;
  text-align: center;
  padding: 14px 26px;
  text-decoration: none;
  color: black;
}
 
li a:hover {
 
}
 
#nav ul {
}
 
#nav li {
}
 
#nav li a {
  color: white;
}
 
#nav li a:hover {
  background-color: #111;
  border-bottom:0.1em solid blue;
}
 
#main{
    width:80%;height:76%;padding:2% 10% 2% 10%;
    background-color:lightgrey;
}
 
#inner {
    min-height: 500px;
    width:95%;height:90%;min-width:800px;
    padding:2% 3%;border-radius:0.5%;
    background-color:white;
}
 
#inner table {
    display:inline-block;
    background-color:lightgrey;
    border-collapse: collapse;
    font-size: 18px;width:50%;height:58%;
 
}
 
#inner th {
    padding:10px 24px;
    background-color: grey
}
 
#inner td {
    padding:10px 24px;
    border-bottom:1px solid grey;
}
 
#inner ul {
}
 
#inner li {
}
 
#inner li a {
    border:1.5px solid grey;
    color:white;
    border-radius:5px;
    margin:8px 8px 0px 8px;
}
 
#inner li a:hover {
  background-color: grey;
}
 
#foot {
    display: table;
    width:96.1%;height:150px;padding:2%;
    background-color:grey;
    color:rgb(32,32,32);
}
 
#foot p {
  display: table-cell;
  line-height: 1.5;
  text-align: right;
  vertical-align: bottom;
}
 
cs

 

style.css

1
2
3
4
5
6
7
8
9
10
.default {
    background-color: white
}
.hover {
    background-color: lightblue
}
.clicked {
    background-color: blue;
    color:white;
}
 

 

mongoGraphQl.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const Express = require("express");
const ExpressGraphQL = require("express-graphql");
const Mongoose = require("mongoose");
const {
    GraphQLID,
    GraphQLString,
    GraphQLList,
    GraphQLNonNull,
    GraphQLObjectType,
    GraphQLSchema
= require("graphql");
 
let dbConfig = {
  user: "test" || "realserver",
  password: "test" || "realserver",
  server: "url:port" || "realserver",
  dbname: "test" || "realserver",
  url: this.user+":"+this.password+"@"+this.server+"/"+this.dbname,
  standalone: "localhost:27017/test"
}
 
async function dbConn() {
  var conn = Mongoose.connect("mongodb://"+dbConfig.standalone, {useUnifiedTopology: true, useNewUrlParser: true,})
}
 
/*
  });
});
  Mongoose.connection.db.collection("member", function(err, collection){
      collection.find({}).toArray(function(err, data){
      })
  });
});*/
 
const MemberModel = Mongoose.model("member"new Mongoose.Schema({
    mid: String,
    nameString,
    description: String
}), "member");
 
const MemberType = new GraphQLObjectType({
    name"Member",
    fields: {
        id: { type: GraphQLID },
        mid: { type: GraphQLString },
        name: { type: GraphQLString },
        description: { type: GraphQLString }
    }
});
 
/*
{
    members(mid:"2"){
        mid,
        name,
        description
    }
    member(id: "5e7f47ea70dc8717101826cf") {
        name
    }
}
mutation createMember {
    member(mid: "1", name: "hi", description: "hello") {
        mid,
        name,
        description
    }
}
*/
 
const testData = [{_id:"12323", mid:"1"name:"hi", description:"good day"},
                  {_id:"23123", mid:"2"name:"hello", description:"good morning"}];
const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name"Query",
        fields: {
            members: {
                type: GraphQLList(MemberType),
                args: {
                   mid: { type: GraphQLString } 
                },
                resolve: (root, args, context, info) => {
                  //return testData;
                  return MemberModel.find(args).exec();
                }
            },
            member: {
                type: MemberType,
                args: {
                    id: { type: GraphQLNonNull(GraphQLID) }
                },
                resolve: (root, args, context, info) => {
                  //return testData[0];
                  return MemberModel.findById(args.id).exec();
                }
            }
        }
    }),
    mutation: new GraphQLObjectType({
        name"Mutation",
        fields: {
            member: {
                type: MemberType,
                args: {
                    mid: { type: GraphQLNonNull(GraphQLString) },
                    name: { type: GraphQLNonNull(GraphQLString) },
                    description: { type: GraphQLNonNull(GraphQLString) }
                },
                resolve: (root, args, context, info) => {
                    var member = new MemberModel(args);
                    return member.save();
                }
            }
        }
    })
});
 
var app = Express();
const cors = require('cors');
const port = 4000;
 
// Start the webserver
async function WebServerOn() {
  app.use("/graphql", ExpressGraphQL({
      schema: schema,
      graphiql: true
  }));
 
 
      console.log("Listening at : "+port+"...");
  });
}
 
// Do it
async function run() {
  await dbConn();
  await WebServerOn();
}
 
run();
 

 

apolloClient.js

1
2
3
4
5
6
7
import ApolloClient from 'apollo-boost';
 
export default new ApolloClient({
 
  uri: "http://localhost:4000/graphql",
  
});
 
 

 

postViewer.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React from 'react';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
 
export const GET_MEMBERS = gql`
{
  members{
      mid,
      name,
      description
  }
}
`;
 
export default () => (
  <Query query={GET_MEMBERS}>
    {({ loading, data, error }) => {
      if (loading) return "gql Loading...";
      if (error) return `gql Loading failed[ ${error.message} ]`;
      return (
 
        <table>
          <thead>
            <tr>
              <th>mid</th>
              <th>name</th>
              <th>description</th>
            </tr>
          </thead>
          <tbody>
 
            {data.members.map(member => (
              <tr key={member.mid}>
                <td>{member.mid}</td>
                <td>{member.name}</td>
                <td>{member.description}</td>
              </tr>
            ))}
 
          </tbody>
        </table>
      );
    }}
  </Query>
);
 
 

 

여기까지 완료하셨다면 Ctrl+B 단축키를 사용하여

다음과 같은 화면을 확인 할 수 있습니다

 

이젠 DB연동을 위한 작업을 진행해보겠습니다

 

먼저 몽고DB를 설치하겠습니다. 

key:value 형태로 document를 관리하는 NoSQL형태의 DB입니다

https://www.mongodb.com/download-center/community

설치를 완료하셨다면

test라는 database를 생성한 후

member라는 컬렉션을 생성하겠습니다

 

그리고

member 컬렉션에 접근하여

ADD DATA를 통하여 (document형) 데이터를 추가해보겠습니다

데이터는 다음과 같은 JSON 타입 형태로 추가합니다

1
2
3
4
5
6
{
 
  "name" : "first",
  "description" : "test data"  
 
}
 

 

여기까지 완료되셨다면

package.json에서 scripts 부분을 다음과 같이 수정합니다

1
2
3
4
5
6
7
"scripts": {
  "start": "react-scripts start && node src/services/servers/mongoGraphQl",
  "watch:server": "nodemon --watch src/services/servers/mongoGraphQl",
  "build": "react-scripts build",
  "test": "react-scripts test --env=jsdom",
  "eject": "taskkill -F -IM node.exe"
},
 
cs

 

비로서 graphQL+MongoDB 서버를 구축하였습니다

허나 서버를 이용하여 화면에 출력하기 위해선 추가적인 작업이 필요합니다 

Apollo라는 패키지를 이용하여 작업을 진행해보겠습니다

 

다음 패키지를 추가적으로

yarn add 해줍니다
graphql-tag
apollo-boost
react-apollo

 

또 다음 소스들을 수정합니다

 

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
 
import { ApolloProvider } from 'react-apollo';
import client from './services/servers/apolloClient';
 
  <React.StrictMode>
      <ApolloProvider client={client}>
        <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
 
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
 

 

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react';
import './App.css';
 
import {Head, Nav, Main, Foot} from './layout/layout';
import Main1 from './layout/main';
import './resources/css/style.css';
import './resources/css/main.css';
 
import PostViewer from './services/postViewer';
 
function App() {
  return (
    <div id="body" className="App">
        <Head />
        <Nav />
 
        <PostViewer />
 
        <Foot />
    </div>
  );
}
 
export default App;
 
 

 

후 서버를 실행하게되면

다음과 같은 화면을 확인 할 수 있습니다

 

다음 시간에는 이어서 main의 CURD 기능들을

좀 더 구체화하도록 하겠습니다

3탄에서는 배포한 사이트의 서비스들을 구축하도록 하겠습니다

 

우선 레이아웃부터 잡아보겠습니다

UI는 이전 시간에 제작한 것을 토대로 사용하겠습니다

 

 

custom.php (외부 css 파일입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
html, body {
    height:100%;min-width:950px;
    margin:0
}
 
span, label {
    display:inline-block;margin:6px 25px 8px 25px;
    font-size:18px;
}
 
input {
    padding:5px;margin-top:-15px
}
 
#head {
    width:100%;height:60px;
    background-color:white;
}
 
#head a {
    display: inline-block;margin-left:20px;
    text-decoration: none;color:black;
}
 
#head button {
    float: right;margin: 8px 10%;
    font-size: 20px;padding:8px 15px;
    background-color: transparent;
    border:0.1em solid #333;border-radius:8px;
}
 
#head button:hover {;
    background-color: #333;
    color:white;
}
 
#nav {
    width:100%;
    border-top:1px solid lightgrey;
    background-color: #333;
    text-align:center;
    margin-bottom: -5px;
}
 
ul {
  display:inline-block;
  list-style-type:none;
  margin:0;padding:0;overflow:hidden;
  background-color: transparent;
}
 
li {
  float: left;
}
 
li a {
  display: block;
  text-align: center;
  padding: 14px 26px;
  text-decoration: none;
  color: black;
}
 
li a:hover {
 
}
 
#nav ul {
}
 
#nav li {
}
 
#nav li a {
  color: white;
}
 
#nav li a:hover {
  background-color: #111;
  border-bottom:0.1em solid blue;
}
 
#body{
    width:80%;height:76%;padding:2% 10% 2% 10%;
    background-color:lightgrey;
}
 
#main {
    width:95%;height:90%;min-width:800px;
    padding:2% 3%;border-radius:0.5%;
    background-color:white;
}
 
#main table {
    display:inline-block;
    background-color:lightgrey;
    border-collapse: collapse;
    font-size: 18px;width:50%;height:58%;
 
}
 
#main th {
    padding:10px 24px;
    background-color: grey
}
 
#main td {
    padding:10px 24px;
    border-bottom:1px solid grey;
}
 
#main ul {
}
 
#main li {
}
 
#main li a {
    border:1.5px solid grey;
    color:white;
    border-radius:5px;
    margin:8px 8px 0px 8px;
}
 
#main li a:hover {
  background-color: grey;
}
 
#foot {
    display: table;
    width:96.1%;height:150px;padding:2%;
    background-color:grey;
    color:rgb(32,32,32);
}
 
#foot p {
  display: table-cell;
  line-height: 1.5;
  text-align: right;
  vertical-align: bottom;
}
 

 

ref.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
<head>
    <style>
    <?php include 
        $_SERVER['DOCUMENT_ROOT'].('/static/css/custom.php');          //외부 서버
        //$_SERVER['DOCUMENT_ROOT'].('/wbtest/static/css/custom.php');   //로컬 서버
    ?>
    </style>
    
    <link rel="stylesheet" type="text/css" href="">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>wbtest</title>
 
</head>
 
<? 
    $root = '';          //외부 서버
    //$root = '/wbtest';   //로컬 서버
?>
 
 

 

head.php

1
2
<a href=""><h2>TEST Site</h2></a>
<button>메뉴6</button>
 

 

nav.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ul class="hlist">
    <li><a href="javascript:void(0);" onclick="locate('<?= $root ?>/main/detail.php')">list</a></li>
    <li><a href="">메뉴2</a></li>
    <li><a href="">메뉴3</a></li>
    <li><a href="">메뉴4</a></li>
    <li><a href="">메뉴5</a></li>
</ul>
 
<script type="text/javascript">
 
function locate(goto) {
}
</script>
 
 

 

top.jsp (상단의 소스를 구성하는 layout페이지입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php include 'ref.php';?>
<body>
 
    <div id="head">
        <?php include 'head.php';?>
    </div>
    
    <div id="nav">
        <?php include 'nav.php';?>
    </div>
 
    <div id="body">
       <div id="main" style="max-width:1050px">
 

 

bottom.jsp

1
2
3
4
5
6
7
8
9
10
11
12
    
        </div>
    </div>
 
    <div id="foot">
 
        <p>copyright by webman</p>
 
    </div>
    
</body>
</html>
 

 

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<?php include 'ref.php';?>
<?php include 'layout/top.php';?>
 
 
<div style="display:inline-block;text-align:right;border-radius:5px 5px 0 0;padding:3px 5px 0px 0;width:100%;background-color:black;">
    <span>hi</span>
    <ul class="hlist">
        <li><a href="javascript:void(0);" onclick="doAjax('c.php')">C</a></li>
        <li><a href="javascript:void(0);" onclick="doAjax('u.php')">U</a></li>
        <li><a href="javascript:void(0);" onclick="r()">R</a></li>
        <li><a href="javascript:void(0);" onclick="doAjax('d.php')">D</a></li>
    </ul>
</div>
 
<script>
    $(document).ready(function() {
        ajaxList();
    });
    
    function ajaxList() {
        $.ajax({ 
            url: "<?= $root ?>/actions/list.php"// 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
            type: "GET"// HTTP 요청 메소드(GET, POST 등)  
            contentType : "application/json",
            
            success:function(returnMap){
                var res=JSON.parse(returnMap);
                console.log(res);
                listItems(res);
            },
            
            error:function(jqXHR, status, errorThrown){
                console.log(errorThrown);
            }
        });
    }
    
    function listItems(listData) {
      $("table > tr").remove();
      var temp, clon;
      var dbData = listData;
      for(var i=0; i<dbData.length; i++) {
          temp = document.getElementsByTagName("template")[0];
          clone = temp.content.cloneNode(true);
          
          td = clone.querySelectorAll("td");
          td[0].textContent = dbData[i].id;
          td[1].textContent = dbData[i].name;
          td[2].textContent = dbData[i].description;
 
          $("table").append(clone);
      }
    }
    
    function doAjax(url){
        //location.href=url;
        var params = $("#form").serialize();
        $.ajax({ 
            url: "<?= $root ?>/actions/"+url, // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
            data: params, // HTTP 요청과 함께 서버로 보낼 데이터 
            type: "POST"// HTTP 요청 메소드(GET, POST 등)  
            //contentType : "application/json",
            
            success:function(returnMap){
                alert(returnMap);
                console.log(returnMap);
                ajaxList();
            },
            
            error:function(jqXHR, status, errorThrown){
                alert(errorThrown);
                console.log(errorThrown);
                ajaxList();
            }
        });
    }
</script>
 
<form name="form" id="form" style="margin:0px;">
 
<div style="display:inline-block;height:100%;width:100%;">
    <table cellpadding="0" cellspacing="0">
        <tr>
            <th>id</th>
            <th>name</th>
            <th>description</th>
            <th>ref</th>
 
        </tr>
    
    </table>
 
    <div style="float:right;height:50%;width:46%;margin:1.8%;">
        <ul>
            <li>
                <label>id</label><input type="" name="id" placeholder="Id는 자동생성 됩니다" readonly>
            </li>
            <li>
                <label>name</label><input type="" name="name">
            </li>
            <li>
                <label>description</label><input type="" name="description">
            </li>
            <li>
                <label>ref</label><input type="" name="">
            </li>
        </ul>
    </div>
 
</div>
 
</form>
 
<template id="list">
    <tr>
        <td class="default"></td>
        <td class="default"></td>
        <td class="default"></td>
        <td class="default"></td>
    </tr>
</template>
 
<style>
    .default {
        background-color: white
    }
    .hover {
        background-color: lightblue
    }
    .clicked {
        background-color: blue;
        color:white;
    }
</style>
 
<script>
    function r() {
        $('input[name=id]').val($('.clicked:eq(0)').html());
        $('input[name=name]').val($('.clicked:eq(1)').html());
        $('input[name=description]').val($('.clicked:eq(2)').html());
    }
    
    $("#main td").mouseover(function(e){
        $(e.target).parent().children('td').removeClass('default');
        $(e.target).parent().children('td').addClass('hover');
    });
    $("#main td").mouseout(function(e){
        $(e.target).parent().children('td').removeClass('hover');
        $(e.target).parent().children('td').addClass('default');
    });
 
    $(document).click(function(e) {
        if($(e.target).is("#main td")) {
            
            if($(e.target).hasClass('clicked')) {
                $('td').removeClass();
                $('td').addClass('default');
                
            } else {
                $('td').removeClass();
                $('td').addClass('default');
                
                $(e.target).parent().children('td').removeClass();
                $(e.target).parent().children('td').addClass('clicked');
                
                //$('input[name=id]').val($(e.target).siblings(':nth-child(1)').html());
                //$('input[name=name]').val($(e.target).siblings(':nth-child(2)').html());
                //$('input[name=description]').val($(e.target).siblings(':nth-child(3)').html());
            }
        } else {
 
        }
    });
</script>
 
 
<?php include 'layout/bottom.php';?>
 
 

 

소스를 구성하고 MAMP를 통해 로컬 서버를 실행하면 다음과 같이 출력됩니다

 

이제는 서비스 로직을 구성해보겠습니다

서비스 소스는 actions 폴더 하위에 배치하도록 하겠습니다

 

서비스를 구성하기전에 db를 먼저 구성하겠습니다

호스팅 사이트에서 제공해주는 외부 mysql server를 사용하겠습니다

 

컨트롤 패널에 접속합니다

 

MySQL Databases에서 새 db를 생성하겠습니다

그리고 phpMyAdmin에서 db를 제어할 것 입니다 

 

db를 생성하시면 

db 연동에 필요한 정보(servername, username, password, dbname)를 제공해줍니다

기억해둡시다

 

아까 말씀드렸듯이 phpMyAdmin을 통해 db를 제어합니다

다음과 같이 테이블을 생성하겠습니다

 

id 컬럼에 기본키와 A.I.(auto increment)를 지정해줍니다

 

저장하여 테이블을 생성합니다

그리고 db연동을 위한 소스를 작성해보겠습니다

아이러니하게도

호스팅 사이트에서는 db연결이 성공적이었음에도

localhost에서는 db에 접근이 되질 않습니다

아무래도 호스팅 사이트의 Apache서버와

localhost의 MAMP 간 php설정 차이 있는 듯 싶습니다

 

 

그래서 MAMP의 mysql 서버를 활용하여 사이트를 구축하고

정식으로 배포할때 외부 mysql로 연결하겠습니다

 

그러므로

잠시 로컬 서버를 사용하기 위한 설정을 하겠습니다

 

그리고 

외부 db에 member 테이블을 생성했듯이

로컬 db에 동일하게 생성하겠습니다

 

MAMP가 작동하는 상태에서

localhost/phpMyAdmin 으로 접속하여 db를 제어할 수 있습니다

 

지금부터는 actions 하위에 서비스단을 구성해보겠습니다

 

conn.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
//header('Content-Type: text/plain; charset=utf-8');
 
/*
$servername = "sql111.ezyro.com"; //외부 서버
$username = 'ezyro_25376976';
$password = 'wkec7pdnf';
$db = "ezyro_25376976_wb";
$port = 3306;
*/
 
$servername = "localhost"; //로컬 서버
$username = 'root';
$password = 'root';
$db = "test";
$port = 3306; 
 
$conn = mysqli_init();
mysqli_real_connect(
   $conn, 
   $servername, 
   $username, 
   $password, 
   $db,
   $port
) or die("DB Connect failed");
 
//echo "mysql://$servername:$port/$username/$db Connected\n";
 
?>
 
 

 

list.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php include 'conn.php';
//header("Content-Type: application/json"); 
 
$arr1 = array();
 
// List 
$result = mysqli_query($conn"SELECT * FROM member");
while ($row = mysqli_fetch_array($result)) {
 
    $arr = array("id" => $row["id"], "name" => $row["name"], "description" => $row["description"]);
    array_push($arr1$arr);
};
 
echo json_encode($arr1);
 
mysqli_close($conn);
 
?>
 
 

 

c.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php include 'conn.php';
//header("Content-Type: application/json"); php
 
$method = $_SERVER['REQUEST_METHOD']; 
$id = "";
$name = ""
$description = ""
$arr = "";
 
if($method == "GET") { 
 
    $id = $_GET['id']; 
    $name = $_GET['name']; 
    $description = $_GET['description']; 
 
else if($method == "POST") { 
 
    $id = $_POST['id']; 
    $name = $_POST['name']; 
    $description = $_POST['description']; 
}
 
$result = mysqli_query($conn"SELECT MAX(id) AS a FROM member");
$row = mysqli_fetch_array($result);
 
$arr = array("id" => $row["a"]+1"name" => $name"description" => $description);
echo json_encode($arr);
 
// Insert 
$sql = "INSERT INTO member (name, description) VALUES ('$name', '$description')";
if (mysqli_query($conn$sql)) {
    echo "\nData Inserted ";
else {
    echo "\nInsert Error: " . mysqli_error($conn);
}
 
mysqli_close($conn);
 
?>
 

 

u.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php include 'conn.php';
 
$method = $_SERVER['REQUEST_METHOD']; 
$id = "";
$name = ""
$description = ""
$arr = "";
 
if($method == "GET") { 
 
    $id = $_GET['id']; 
    $name = $_GET['name']; 
    $description = $_GET['description']; 
 
else if($method == "POST") { 
 
    $id = $_POST['id']; 
    $name = $_POST['name']; 
    $description = $_POST['description']; 
}
 
$arr = array("id" => $id"name" => $name"description" => $description);
echo json_encode($arr);
 
// Update 
$sql = "UPDATE member SET name='$name', description='$description' WHERE id='$id'";
if (mysqli_query($conn$sql)) {
    echo "\nData Updated";
else {
    echo "\nUpdate Error: " . mysqli_error($conn);
}
 
mysqli_close($conn);
 
?>
 
 

 

d.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php include 'conn.php';
 
$method = $_SERVER['REQUEST_METHOD']; 
$id = "";
 
if($method == "GET") { 
 
    $id = $_GET['id']; 
 
else if($method == "POST") { 
 
    $id = $_POST['id']; 
}
 
// Delete 
$sql = "DELETE FROM member WHERE id='$id'";
if (mysqli_query($conn$sql)) {
    echo "id=$id Data Deleted";
else {
    echo "Delete Error: " . mysqli_error($conn);
}
 
mysqli_close($conn);
 
?>
 

 

여기까지 완성하셨다면 로컬 서버에서

다음과 같은 결과를 확인 할 수 있습니다

 

 

구축한 사이트가

테스트서버에서 정상적으로 동작하므로

실서버에 배포하도록 하겠습니다

 

먼저 실서버 구동을 위한 설정을 하겠습니다

그리고 sublime FTP Upload Folder를 실행하여

실서버에 소스를 업데이트 합니다

 

 

완료하셨다면 

실서버에서 다음과 같은 결과를 확인 할 수 있습니다

사이트를 구축하기 이전에

서비스 배포를 먼저 진행해보겠습니다

아래 사이트에서 진행해보겠습니다

 

https://profreehost.com/

 

회원가입 후 

새로운 사이트를 추가하겠습니다

 

 

사이트를 성공적으로 생성하셨다면 다음과 같은 관리 탭이 보이실겁니다

 

 

관리 탭에서

FTP 서버 관련 정보를 확인 할 수 있습니다

 

 

생성한 FTP 서버를 Sublime Text와 연동해보려고 합니다

Package Control을 통해 sftp plugin을 설치합니다

 

 

그리고 서브라임에 프로젝트를 연결하겠습니다

폴더의 경로는 설치하신 php htdocs 하위경로로 두시는게 좋습니다

MAMP를 사용할 경우 C:\MAMP\htdocs\ 하위에 두시면 되겠습니다

 

 

해당 폴더를 선택 후 Map to Remote을 진행합니다

그러면 sftp-config.json이 생성되며 

여기에 연동할 서버 정보를 입력해줍니다

 

type, host, user, password, port, remote_path

정도만 작성하면 되겠습니다

 

 

폴더 FTP/Download Folder를 통해

다음과 같이 서버의 폴더들을 성공적으로 내려받았다면 연동에 성공한 것입니다

 

서브라임만으로 FTP서버의 파일들을 관리하는 것이 불편할 수 있습니다

그럴때 파일질러와 같은 FTP Client를 같이 사용하셔도 좋습니다

 

https://filezilla-project.org/download.php?type=client

 

사이트의 대문 페이지는 Index.php로 설정 되어있습니다

Index.php가 존재하지 않다면 index2.html을 실행하게 됩니다

그러므로 먼저 index.php를 생성해보도록 해보겠습니다

 

index2.html의 소스를 index.php로 그대로 옮기고

아래와 같이 body의 내용을 간단하게 구성하겠습니다

 

 

해당 파일 FTP/Upload File을 통해

서버의 프로젝트를 업데이트하고 사이트에서 확인 할 수도 있습니다

 

 

하지만 매번 수정할때마다 업로드하기에 불편하므로

로컬 서버에서 바로 확인 할 수 있도록 구성해보겠습니다 

 

Package Control을 통해 View In Browser plugin를 설치합니다

https://packagecontrol.io/packages/View%20In%20Browser

 

프로젝트를 저장하면

Edit Project로 build를 제어할 수 있습니다

다음과 같이 작성해줍니다

 

 

그리고 Ctrll+Alt+C 를 누르시면 

로컬 서버에서 프로젝트를 확인 할 수 있습니다

 

웹맨의 웹개발 놀이터    https://wbman.tistory.com/

시염둥이의 시염시염      https://cyum2.tistory.com/

 

신나게, 감사하게           https://joynthanks.tistory.com

 

 

1) 개발환경 구성(MAMP)

 

https://www.mamp.info/en/windows/

에서 MAMP PRO를 다운 받아 설치를 진행합니다

설치시 MAMP PRO는 유료버전이므로 체크하지 않도록 합시다

 

설치 후 MAMP를 실행하면 다음과 같은 인터페이스를 확인 할 수 있습니다 

 

 

그리고 서버가 실행되어 있는 상태에서

브라우저를 띄워 localhost로 접속하면

Root 경로에 index.php를 랜더링한 endpoint 결과를 확인 할 수 있습니다  

 

 

또한

http://localhost/phpMyAdmin/

에서 MySQL Data Base를 관리 할 수 있습니다

 

 

아주 쉽죠?

 

2) 개발환경 구성(apache)

다음은 보다 복잡한 방법이므로 처음 하시는 분들은 어려우실 수 있습니다 

 

php7.2 

https://windows.php.net/download/#php-7.2-nts-VC15-x64

apache2.4 

https://www.apachelounge.com/download/

 

 

 

 

 

 

 

 

 

php, apache 를 다운받아 폴더를 C:\ 의 하위 경로로 배치하고

php.ini 및 httpd.conf 설정을 해줍니다 

 

PHPIniDir "C:/php722"
LoadModule php7_module "C:/php722/php7apache2_4.dll"
AddType application/x-httpd-php .html .php
AddHandler application/x-httpd-php .php

 

CLI 에서 실행하기 위해 환경변수 설정 해줍니다

C:\Apache24\bin

Apache Server 실행(php, apache 연동)

httpd -k install

httpd -k start

 

허나 php7apache2_4.dll 이 누락되었다는 에러가 발생합니다

그것은 Non Thread Safe 버전에서는 php7apache2_4.dll 이 누락되어 있기 때문입니다

그래서 Thread Safe 버전으로 사용해야 합니다

다시 다운받아 진행해보겠습니다

 

다운 받은 파일은 기존의 php722와 통째로 교체해줍니다

 

php.ini 를 다시 설정해주시고

 

서버를 다시 실행해보시면

80포트가 MAMP와 겹치기 때문에 다음과 같은 에러가 발생할 것입니다

 

그래서 포트를 81로 변경해주도록 하겠습니다

 

그리고 서버를 실행합니다

사실 ApacheMonitor.exe를 통해

CLI가 아닌 GUI에서도 서버를 실행할 수 있습니다

 

그리고

index.html를 index.php로 변경하셔서 

localhost:81/index.php로 접근하시면

다음과 같은 출력물을 확인 할 수 있습니다

 

이어서 CURD가 가능한 React View를 구성해보도록 하겠습니다 

View는 지난 전자정부 프로젝트 때 제작했던 소스를 재사용하겠습니다

 

추가된 패키지 구성은 다음과 같습니다 

application.yml (Spring Boot 내장 logger 설정을 추가하였습니다)

 

지난 프로젝트의 기존 소스를 그대로 사용하기 위해서

Controller에서 사용했던 Object Mapper와 Dozer Mapper의 의존성을 추가하겠습니다

 

build.gradle

1
2
3
4
    exclude group: 'org.slf4j'
}
 
 

 

우선 CURD 서비스를 수행할 method들을 추가하겠습니다

 

CustomerService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
import org.springframework.stereotype.Service;
 
@Service
public class CustomerService {
    
    @Autowired
    CustomerRepository customerRepository;
 
    public Iterable<Customer> findAll() {
        return customerRepository.findAll();
    }
 
    public Iterable<Customer> selectList(Customer customer) {
        return customerRepository.findAll();
    }
 
    public Customer insert(Customer customer) {
        return customerRepository.save(customer);
    }
 
    public boolean update(Customer customer) {
        return true;
    }
 
    public boolean delete(Customer customer) {
        return true;
    }
}
 
 

 

페이지의 메인단 화면입니다

기존에 사용했던 tiles 대신에 mustache의 include를 이용해 레이아웃을 나눠봤습니다

 

react.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{{>layout/ref}}
 
<script data-plugins="transform-es2015-modules-umd" type="text/babel" src="/resources/script/others.jsx"></script>
<script data-plugins="transform-es2015-modules-umd" type="text/babel" src="/resources/script/main.jsx"></script>
<script data-plugins="transform-es2015-modules-umd" type="text/babel">
import Layout, {Head, Nav, Foot} from './others';
import Main from './main';
 
{{>layout/head}}
 
 
                <Main list="{{dataList}}"/>
                
                
{{>layout/foot}}
 
ReactDOM.render(html, document.getElementById('mydiv'));
 
</script>
 
</html>
 
 

 

사실 프로젝트를 진행하면서 style.css를 잘 인식 못해서

main.css도 포함해서 html에 옮겨 include 하였습니다

 

ref.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <!-- <link rel="stylesheet" href="${pageContext.request.contextpath}/resources/css/style.css"></link>
    <link rel="stylesheet" href="${pageContext.request.contextpath}/resources/css/main.css"></link> -->
    
    <style type="text/css">
        html, body {
            height:100%;
            margin:0;
        }
        
        span, label {
            display:inline-block;margin:6px 25px 8px 25px;
            font-size:18px;
        }
        
        input {
            padding:5px;margin-top:-15px
        }
        
        #head {
            width:100%;height:60px;
            background-color:white;
        }
        
        #head a {
            display: inline-block;margin-left:20px;
            text-decoration: none;color:black;
        }
        
        #head button {
            float: right;margin: 8px 10%;
            font-size: 20px;padding:8px 15px;
            background-color: transparent;
            border:0.1em solid #333;border-radius:8px;
        }
        
        #head button:hover {;
            background-color: #333;
            color:white;
        }
        
        #nav {
            width:100%;
            border-top:1px solid lightgrey;
            background-color: #333;
            text-align:center;
            margin-bottom: -5px;
        }
        
        ul {
          display:inline-block;
          list-style-type:none;
          margin:0;padding:0;overflow:hidden;
          background-color: transparent;
        }
        
        li {
          float: left;
        }
        
        li a {
          display: block;
          text-align: center;
          padding: 14px 26px;
          text-decoration: none;
          color: black;
        }
        
        li a:hover {
        
        }
        
        #nav ul {
        }
        
        #nav li {
        }
        
        #nav li a {
          color: white;
        }
        
        #nav li a:hover {
          background-color: #111;
          border-bottom:0.1em solid blue;
        }
        
        #body{
            width:80%;height:76%;padding:2% 10% 2% 10%;
            background-color:lightgrey;
        }
        
        #main {
            width:88%;height:90%;min-width:800px;min-height:490px;
            padding:2% 6%;border-radius:0.5%;
            background-color:white;
        }
        
        #main table {
            display:inline-block;
            background-color:lightgrey;
            border-collapse: collapse;
            font-size: 18px;width:50%;height:58%;
        
        }
        
        #main th {
            padding:10px 24px;
            background-color: grey
        }
        
        #main td {
            padding:10px 24px;
            border-bottom:1px solid grey;
        }
        
        #main ul {
        }
        
        #main li {
        }
        
        #main li a {
            border:1.5px solid grey;
            color:white;
            border-radius:5px;
            margin:8px 8px 0px 8px;
        }
        
        #main li a:hover {
          background-color: grey;
        }
        
        #foot {
            display: table;
            width:96.1%;height:150px;padding:2%;
            background-color:grey;
            color:rgb(32,32,32);
        }
        
        #foot p {
          display: table-cell;
          line-height: 1.5;
          text-align: right;
          vertical-align: bottom;
        }
        
        .default {
            background-color: white
        }
        .hover {
            background-color: lightblue
        }
        .clicked {
            background-color: blue;
            color:white;
        }
    </style>
    
<script type="text/javascript" src="/resources/script/common.js"></script>
    
    
</head>    
 
<div id="mydiv"></div>
 
 
 

 

head.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const html=
    <body>
 
        <div id="head">
            <Head />
        </div>
        
        <div id="nav">
            <Nav />
        </div>
 
        <div id="body">
            <div id="main">
 
 

 

foot.html

1
2
3
4
5
6
7
8
            </div>
        </div>
 
        <div id="foot">
            <Foot />
        </div>
 
    </body>
s

 

이번 프로젝트는 React로 View를 구성하는 것이므로 

주요 소스들이 .jsx 파일에 있습니다 

 

others.jsx (이번 React View에서 Layout 엘리먼트들을 정의하는 소스입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
export class Head extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div>
 
                <a href="#"><h2>TEST Site</h2></a>
                <button>메뉴6</button>
 
            </div>
        );
    }
}
 
export class Nav extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div>
 
                <ul className="hlist">
                    <li><a href="#" onClick={function(){location.href='/react';}}>list</a></li>
                    <li><a href="#">메뉴2</a></li>
                    <li><a href="#">메뉴3</a></li>
                    <li><a href="#">메뉴4</a></li>
                    <li><a href="#">메뉴5</a></li>
                </ul>
 
            </div>
        );
    }
}
 
export class Foot extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div>
 
                <p>copyright by webman</p>
 
            </div>
        );
    }
}
 
 
export default class Layout extends React.Component {
    constructor(props) {
        super(props);
    }
 
    render() {
        return (
            <div>
 
                <Head />
                <Nav />
                {this.props.list}
                <Foot />
 
            </div>
        );
    }
}
 
 

 

main.jsx (이번 React View에서 Main 화면 엘리먼트를 정의하는 소스입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
function Bar() {
    const style1 = {
            display:'inline-block',
            textAlign:'right',
            borderTopLeftRadius:5,
            paddingTop:3+'px',
            paddingLeft:5+'px',
            width:100+'%',
            backgroundColor:'black'
        };
    
        return (
            <div style={style1}>
 
    <span>hi</span>
    <ul className="hlist">
        <li><a href="#" onClick={function(){doAjax('c.do');}}>C</a></li>
        <li><a href="#" onClick={function(){doAjax('u.do');}}>U</a></li>
        <li><a href="#" onClick={function(){r();}}>R</a></li>
        <li><a href="#" onClick={function(){doAjax('d.do');}}>D</a></li>
    </ul>
 
            </div>
          );
}
 
function Input() {
        
        return (
            <div style={{float:'right',height:50+'%',width:46+'%',margin:1.8+'%'}}>
 
        <ul>
            <li>
                <label>id</label><input type="" name="id" placeholder="Id는 자동생성 됩니다" readOnly/>
            </li>
            <li>
                <label>firstName</label><input type="" name="firstName"/>
            </li>
            <li>
                <label>lastName</label><input type="" name="lastName"/>
            </li>
            <li>
                <label>ref</label><input type="" name=""/>
            </li>
        </ul>
 
            </div>
          );
}
 
function ListItems(list) {
    const array=[];
    $.each(list, function(idx, val) {
        const list=JSON.parse(val);
        for(var i=0; i<list.length; i++) {
            array.push(list[i]);
        }
    });
    console.log(array[0]);{/**/}
 
      return (
          <tbody>
 
        {array.map(function(value, index){
            return (
                <tr>
                    <td className="default" key={index}>{value.id}</td>
                    <td className="default" key={index}>{value.firstName}</td>
                    <td className="default" key={index}>{value.lastName}</td>
                    <td className="default" key={index}></td>
                </tr>
            );
        })}
 
        </tbody>
      );
}
 
function List({list}) {
 
        return (
            <div>
                
    <form name="form" id="form" style={{margin:0+'px'}}>
 
<div style={{display:'inline-block',height:100+'%',width:100+'%'}}>
    <table cellPadding="0" cellSpacing="0">
        
        <tr>
            <th>id</th>
            <th>firstName</th>
            <th>lastName</th>
            <th>ref</th>
 
        </tr>
 
            <ListItems data={list}/>
        
    </table>
 
    <Input/>
 
</div>
 
</form>
 
            </div>
          );
}
 
export default class Main extends React.Component {
    constructor(props) {
        super(props);
    }
 
    Bar() {}
    Input() {}
    List(props) {}
 
    render() {
        return (
            <div>
            
                <Bar />
                <List list={this.props.list}/>
                {/*{this.props.list}*/}
            </div>
        );
    }
}
 
function r() {
    $('input[name=id]').val($('.clicked:eq(0)').html());
    $('input[name=firstName]').val($('.clicked:eq(1)').html());
    $('input[name=lastName]').val($('.clicked:eq(2)').html());
}
 
function ajaxList() {
    $.ajax({ 
        url: "/list.do"// 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
        type: "GET"// HTTP 요청 메소드(GET, POST 등)  
        dataType: "json"// 서버에서 보내줄 데이터의 타입  
        
        success:function(returnMap){
            //console.log(returnMap.api);
            //console.log(returnMap.dbData);
            alert(returnMap.dbData);
            ListItems(returnMap.dbData);
        },
        
        error:function(jqXHR, status, errorThrown){
            //console.log(jqXHR.responseText);
        }
    });
}
 
function doAjax(url){
    var params = $("#form").serialize();
    $.ajax({ 
        url: "/"+url, // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
        data: params, // HTTP 요청과 함께 서버로 보낼 데이터 
        type: "POST"// HTTP 요청 메소드(GET, POST 등)  
        dataType: "json"// 서버에서 보내줄 데이터의 타입  
        
        success:function(returnMap){
            //console.log(returnMap.api);
            ajaxList();
        },
        
        error:function(jqXHR, status, errorThrown){
            //console.log(jqXHR.responseText);
            ajaxList();
        }
    });
}
 
$(document).ready(function() {
    //ajaxList();
 
    $("#main td").mouseover(function(e){
        $(e.target).parent().children('td').removeClass('default');
        $(e.target).parent().children('td').addClass('hover');
    });
    $("#main td").mouseout(function(e){
        $(e.target).parent().children('td').removeClass('hover');
        $(e.target).parent().children('td').addClass('default');
    });
 
    $(document).click(function(e) {
           if($(e.target).is("#main td")) {
        
            if($(e.target).hasClass('clicked')) {
                $('td').removeClass();
                $('td').addClass('default');
            
            } else {
                $('td').removeClass();
                $('td').addClass('default');
            
                $(e.target).parent().children('td').removeClass();
                $(e.target).parent().children('td').addClass('clicked');
            
                //$('input[name=id]').val($(e.target).siblings(':nth-child(1)').html());
                //$('input[name=firstName]').val($(e.target).siblings(':nth-child(2)').html());
                //$('input[name=lastName]').val($(e.target).siblings(':nth-child(3)').html());
            }
        } else {    
 
        }
    });
});    
 
 
 

 

마지막으로 Controller를 구성하도록하겠습니다

 

CustomerController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 
 
import javax.annotation.PostConstruct;
 
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
 
import com.fasterxml.jackson.databind.ObjectMapper;
 
@Controller
@RequestMapping("/")
public class CustomerController {
    
    Logger LOGGER = LoggerFactory.getLogger(CustomerController.class);
    
    @Autowired
    AmazonProperties amazonProperties;
    
    @Autowired
    CustomerService customerService;
    
    @RequestMapping(value = "/react", method=RequestMethod.GET)
    public ModelAndView main(/* Map<String, Object> model, */
                                ModelAndView mav) throws JsonProcessingException, ParseException {
        String id=amazonProperties.getAssociateId();
        
        Iterable<Customer> list=customerService.findAll();
        
        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = mapper.writeValueAsString(list);
        JSONParser parser = new JSONParser();
        JSONArray jsonArr = (JSONArray) parser.parse(jsonStr);
        
        mav.addObject("dataList", jsonArr);
        mav.setViewName("main/react");
        return mav;
    }
    
    Customer customer;
    ObjectMapper oMapper;
    DozerBeanMapper dMapper;
    
    @PostConstruct
    public void initController(){
        customer = new Customer();
        oMapper = new ObjectMapper();
        dMapper = new DozerBeanMapper();
    }
    
    public Iterable<Customer> getList(Customer customer) throws Exception {
        
        return customerService.selectList(customer);
    }
    
    @RequestMapping(value = "/list.do")
    public ResponseEntity<?> getlist(@RequestParam Map<String, Object> paramMap
                                   ) throws Exception {
        LOGGER.debug("getlist() called");
        paramMap.put("api""alist");
 
        paramMap.put("dbData", getList(customer));
        System.out.println(getList(customer));
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/c.do", method={RequestMethod.POST})
    public ResponseEntity<?> create(@RequestParam Map<String, Object> paramMap,
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""create");
        customer = dMapper.map(paramMap, Customer.class);
        LOGGER.debug("create() sampleVo: "+customer);
        
        Customer result=customerService.insert(customer);
        LOGGER.debug("create() result: "+result);
        paramMap.put("result", result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/u.do", method={RequestMethod.POST})
    public ResponseEntity<?> update(@RequestParam Map<String, Object> paramMap, 
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""update");
        customer = dMapper.map(paramMap, Customer.class);
        LOGGER.debug("update() sampleVo: "+customer);
 
        boolean result=customerService.update(customer);
        LOGGER.debug("update() result: "+result);
        paramMap.put("result", result);
        System.out.println(result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/d.do", method={RequestMethod.POST})
    public ResponseEntity<?> delete(@RequestParam Map<String, Object> paramMap, 
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""delete");
        customer = dMapper.map(paramMap, Customer.class);
        LOGGER.debug("delete() sampleVo: "+customer);
        
        boolean result=customerService.delete(customer);
        LOGGER.debug("delete() result: "+result);
        paramMap.put("result", result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
}
 
 
 

 

완성되셨다면 다음과 같은 화면을 확인하실 수 있습니다

 

Ajax로 list를 로딩하는 부분,

MariaDB utf-8 인코딩 설정은 생략되어 있는 점 참고해주시기 바랍니다

수고하셨습니다

 

1탄에 구현한 서비스를 컨트롤러-View에 연동하는 시간을 갖겠습니다

 

View에서 mustache를 사용할것이므로 

우선 build.gradle에 관련 의존성을 추가하겠습니다

 

그럼 다음과 같이 구성해보겠습니다

 

application.properties

 

CustomerService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
import org.springframework.stereotype.Service;
 
@Service
public class CustomerService {
    
    @Autowired
    CustomerRepository customerRepository;
 
    public Iterable<Customer> findAll() {
        return customerRepository.findAll();
    }
}
 
 

 

CustomerController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
import org.springframework.stereotype.Controller;
 
@Controller
@RequestMapping("/")
public class CustomerController {
    
    @Autowired
    AmazonProperties amazonProperties;
    
    @Autowired
    CustomerService customerService;
    
    @RequestMapping(value = "/react", method=RequestMethod.GET)
    public ModelAndView main(/* Map<String, Object> model, */
                                ModelAndView mav) {
        String id=amazonProperties.getAssociateId();
        
        Iterable<Customer> list=customerService.findAll();
        
        mav.addObject("dataList", list);
        mav.setViewName("main/index");
        return mav;
    }
}
 
 
 

AmazonProperties.java

(사실 이 소스는 필수는 아닙니다. 사용 안하신다면 컨트롤러에서 Autowired를 제거해주세요)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
import org.springframework.stereotype.Component;
 
@Component
@ConfigurationProperties("amazon")
public class AmazonProperties {
    
    private String associateId;
    
    public void setAssociateId(String associateId) {
        this.associateId=associateId;
    }
    
    public String getAssociateId() {
        return associateId;
    }
}
 

 

error.html

(Spring Boot에서는 에러페이지가 error.html로 자동 매핑되어있습니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="/resources/css/style.css"></link>
</head>
<body>
 
    <span class="oops">Oops!</span><br />
    <p>There seems to be a problem with the page you requested</p>
 
</body>
</html>
 

 

index.html (우리가 컨트롤러에서 매핑한 View입니다. {{ }} 가 mustache입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="/resources/css/style.css"></link>
</head>    
<body>
     
    {{dataList}}
 
</body>
</html>
 
 

 

style.css (아시다 싶이. 외부 스타일 시트입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
body {
    background-color: #cccccc;
    font-family: arial, helvetica, sans-serif;
}
 
.bookHeadline {
    font-size: 12pt;
    font-weight: bold;
}
 
.bookDescription {
    font-size: 10pt;
}
 
label {
    font-weight: bold;
}
 
.error {
    color: red;
}
 
.errorPage {
    text-align: center;
}
 
.oops {
    font-size: 76pt;
}
 
 

 

여기까지 완성하셨다면 서버를 실행해보겠습니다

그 전에 ~Application.java 소스를 한번 체크하고 가겠습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
 
@SpringBootApplication
public class Example2Application implements WebMvcConfigurer, CommandLineRunner {
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //welcomePage
        //registry.addViewController("/login").setViewName("login");
    }
    
    @PersistenceContext
    EntityManager entityManager;
    
    @Autowired
    CustomerRepository repository;
    
    @Override
    public void run(String... args) throws Exception {
        //entityManager.createNativeQuery("create table customer (id bigint not null, first_name varchar(255), last_name varchar(255), primary key (id)) engine=InnoDB").executeUpdate();
        
        repository.save(new Customer("Jack""Bauer"));
        repository.save(new Customer("Chloe""O'Brian"));
        repository.save(new Customer("Kim""Bauer"));
        repository.save(new Customer("David""Palmer"));
        repository.save(new Customer("Michelle""Dessler"));
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Example2Application.class, args);
    }
}
 

 

Spring Boot App으로 실행하고 나면

 

다음과 같은 결과를 확인 할 수 있습니다.

2탄은 여기까지 하겠습니다. 

이어서 3탄에서는 CURD가 가능한 React View를 구성해보도록 하겠습니다 

서버 구동시 DataBase DataSource정보가 필요한 

Spring Boot의 특성을 고려하여

새프로젝트를 생성하기전에 DataBase를 먼저 준비하겠습니다

MariaDB를 사용할것이므로 아래 링크에서 설치파일을 받아 설치를 진행합시다

 

https://downloads.mariadb.org/mariadb/10.2.31/

 

설치시 기재하는 포트정보는 이후에 필요할 수 있으므로 기억해둡시다

이는 모든 DB에 해당합니다

 

 

MariaDB에 접근 할때는

바탕화면에 바로가기 아이콘이 없을 시

작업표시줄에 위치한 검색 탭에서 HeidiSQL을 검색하여, 찾아 실행합시다 (윈도10에 해당)

 

 

정상적으로 실행되면 다음과 같은 화면을 확인 할 수 있습니다

사실 MariaDB의 경우 포트를 쉽게 변경할 수도 있습니다

 

 

이제 gradle 기반으로 Spring Boot 프로젝트를 생성해보도록 해보겠습니다

 

 

Spring Boot의 특징 중 하나는

프로젝트를 생성할때 의존성을 미리 설정해줄 수 있습니다

여기선 MariaDB 의존성 설정이 없기 때문에

테스트를 위해 우선 MySQL 그리고 JPA 의존성을 추가해줬습니다  

 

 

프로젝트 생성이 완료되면

다음과 같은 구성을 확인 할 수 있습니다

 

~Application.java 우클릭 Spring Boot App을 선택하여

서버도 실행해보겠습니다

 

보시는 바와 같이 에러가 발생합니다. 에러의 원인은

맨 하단에 embedded(내장된) database 설정을 해줘야한다

고 나오네요  

 

 

그래서 프로젝트를 다시 생성하겠습니다

이 후 DB 설정도 하겠습니다

 

 

프로젝트 생성 후 우선

gradle 프로젝트 build 파일에 JPA와 MariaDB 의존성을 추가 하겠습니다

 

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
plugins {
    id 'java'
}
 
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
 
repositories {
    mavenCentral()
}
 
dependencies {
    
    
    testImplementation 'junit:junit:4.12'
    
    }
}
 
test {
    useJUnitPlatform()
}
 
 

 

그리고 DB연동에 필요한 Data Source 정보를

yml이나 properties 파일에 기재합니다

참고로 yml과 properties의 작성 방법(문법)은 다릅니다

 

application.yml

 

이 다음에는 JPA Interface를 implements하는 Repository를 꼭 생성해주어야 합니다

Repository가 사용할 DTO도 필요할 것입니다

 

CustomerRepository.java (Repository Class 입니다)

1
2
3
4
5
6
7
8
 
 
public interface CustomerRepository extends CrudRepository<Customer, Long>{
    
}
 

 

Customer.java (DTO로 사용할 Class입니다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
 
@Entity
@Table(name = "Customer")
public class Customer {
 
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  
  @Column(name = "id")
  private Long id;
  
  @Column(name = "firstName")
  private String firstName;
  
  @Column(name = "lastName")
  private String lastName;
 
  protected Customer() {}
 
  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
 
  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }
 
  public Long getId() {
    return id;
  }
 
  public String getFirstName() {
    return firstName;
  }
 
  public String getLastName() {
    return lastName;
  }
}
 
 

 

여기까지 하셨다면 JPA Hibernate를 사용할 준비가 되셨습니다

그러면 우리가 구현한 소스들이 서로 연동이 잘되는지

JUnit으로 테스트를 진행해보겠습니다

 

@Test 소스를 작성하고

 

~ApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 
 
import javax.annotation.PostConstruct;
 
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:application.properties")
class Example2ApplicationTests {
    @Value("${spring.datasource.url}")
    private String URL;
    private String DRIVERNAME;
    @Value("${spring.datasource.username}")
    private String USERNAME;
    @Value("${spring.datasource.password}")
    private String PASSWORD;
 
    @Test
    void connTest() throws SQLException, ClassNotFoundException {
        Class.forName(DRIVERNAME);
        try(Connection conn = 
                DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
            System.out.println("connTest: "+conn);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    @Autowired
    CustomerRepository customerRepository;
    
    @PostConstruct
    public void init() {
        customerRepository.save(new Customer("Jack""Bauer"));
        customerRepository.save(new Customer("Chloe""O'Brian"));
        customerRepository.save(new Customer("Kim""Bauer"));
        customerRepository.save(new Customer("David""Palmer"));
        customerRepository.save(new Customer("Michelle""Dessler"));
    }
    
    @Test
    void queryTest() throws SQLException, ClassNotFoundException {
        Optional<Customer> result=customerRepository.findById(1L);
        System.out.println("queryTest: "+result);
    }
}
 
 

 

다음과 같이 JUnit을 실행합니다

 

여기까지 잘 따라오셨다면 다음과 같은 결과를 확인 할 수 있을 것입니다

 

2탄에서는 컨트롤러와 View를 연결하는 시간을 갖도록 하겠습니다

그럼 이만

지난 시간에 이어 CURD 연동 소스를 마저 구현해보도록 하겠습니다.

 

서비스 소스가 정상적으로 잘 작동이 된다면,

Controller 소스를 작성하면 되겠습니다.

 

앞서 몇가지 필요한 라이브러리를 메이븐에 추가하도록 하겠습니다.

 

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
        
        <!-- 오브젝트매퍼: https://mvnrepository.com/artifact/net.sf.dozer/dozer -->
        <dependency>
            <groupId>net.sf.dozer</groupId>
            <artifactId>dozer</artifactId>
            <version>5.4.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
 
 

 

컨트롤러에서

ObjectMapper를 사용하기 위해 databind, 

DozerMapper를 사용하기 위해 dozer 를 추가 하였습니다.

 

ObjectMapper는 객체를 JSON String으로 변환할때,

DozerMapper는 Map객체를 DTO객체에 bind 할 목적으로 사용할 것입니다. 

 

EgovSampleController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 
 
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
 
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
 
import com.fasterxml.jackson.databind.ObjectMapper;
 
 
@Controller
@RequestMapping("/sample")
public class EgovSampleController {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(EgovSampleController.class);
    
    @Resource(name = "sampleService")
    EgovSampleService egovSampleService;
    
    SampleDefaultVO sampleDefaultVO;
    SampleVO sampleVo;
    ObjectMapper oMapper;
    DozerBeanMapper dMapper;
    
    @PostConstruct
    public void initController(){
        sampleDefaultVO = new SampleDefaultVO();
        sampleVo = new SampleVO();
        oMapper = new ObjectMapper();
        dMapper = new DozerBeanMapper();
    }
 
    /**
     * 글 목록을 조회한다. (paging)
     * @param searchVO - 조회할 정보가 담긴 SampleDefaultVO
     * @param model
     * @return "egovSampleList"
     * @exception Exception
     */
    @RequestMapping(value = "/main.do")
    public String listMain(ModelMap modelMap) throws Exception {
        LOGGER.debug("listMain() called");
//        sampleDefaultVO = new SampleDefaultVO();
//        List<?> dbData = egovSampleService.selectSampleList(sampleDefaultVO);
//        
//        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dbData);
//        modelMap.addAttribute("dbData", jsonString);
 
        return "sample/curd.tiles";
    }
    
    public List<?> getList(SampleDefaultVO sampleDefaultVO) throws Exception {
        sampleDefaultVO = new SampleDefaultVO();
        
        return egovSampleService.selectSampleList(sampleDefaultVO);
    }
    
    @RequestMapping(value = "/list.do")
    public ResponseEntity<?> getlist(@RequestParam Map<String, Object> paramMap
                                   ) throws Exception {
        LOGGER.debug("getlist() called");
        paramMap.put("api""alist");
 
        paramMap.put("dbData", getList(sampleDefaultVO));
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/c.do", method={RequestMethod.POST})
    public ResponseEntity<?> create(@RequestParam Map<String, Object> paramMap,
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""create");
        sampleVo = dMapper.map(paramMap, SampleVO.class);
        LOGGER.debug("create() sampleVo: "+sampleVo);
        
        String result=egovSampleService.insertSample(sampleVo);
        LOGGER.debug("create() result: "+result);
        paramMap.put("result", result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/u.do", method={RequestMethod.POST})
    public ResponseEntity<?> update(@RequestParam Map<String, Object> paramMap, 
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""update");
        sampleVo = dMapper.map(paramMap, SampleVO.class);
        LOGGER.debug("update() sampleVo: "+sampleVo);
 
        boolean result=egovSampleService.updateSample(sampleVo);
        LOGGER.debug("update() result: "+result);
        paramMap.put("result", result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/d.do", method={RequestMethod.POST})
    public ResponseEntity<?> delete(@RequestParam Map<String, Object> paramMap, 
                                    HttpServletRequest request
                                    ) throws Throwable {
        paramMap.put("api""delete");
        sampleVo = dMapper.map(paramMap, SampleVO.class);
        LOGGER.debug("delete() sampleVo: "+sampleVo);
        
        boolean result=egovSampleService.deleteSample(sampleVo);
        LOGGER.debug("delete() result: "+result);
        paramMap.put("result", result);
        
        String jsonString = oMapper.writerWithDefaultPrettyPrinter().writeValueAsString(paramMap);
        return new ResponseEntity<String>(jsonString, HttpStatus.OK);
    }
}
 
 

 

컨트롤러의 Mapping url값을 변경하였으므로,

첫화면으로 포워딩해주는 index.jsp도 같이 수정해줍니다.

 

1
2
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<jsp:forward page="sample/main.do"/>
 

 

List를 출력할때 JSTL을 활용할까 고심끝에

List를 새로고침이 필요없는 비동기 처리 하는게 좋을거 같아

앞서 보신 소스와 같이 Java의 Object Mapper와 HTML의 template tag 를 이용해

List를 출력해보겠습니다.

 

view에도 필요한 소스를 추가하겠습니다.

 

curd.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 
<div style="display:inline-block;text-align:right;border-radius:5px 5px 0 0;padding:3px 5px 0px 0;width:100%;background-color:black;">
    <span>hi</span>
    <ul class="hlist">
        <li><a href="javascript:void(0);" onclick="doAjax('c.do')">C</a></li>
        <li><a href="javascript:void(0);" onclick="doAjax('u.do')">U</a></li>
        <li><a href="javascript:void(0);" onclick="r()">R</a></li>
        <li><a href="javascript:void(0);" onclick="doAjax('d.do')">D</a></li>
    </ul>
</div>
 
<script>
    $(document).ready(function() {
        ajaxList();
    });
    
    function ajaxList() {
        $.ajax({ 
            url: "${pageContext.request.contextPath}/sample/list.do"// 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
            type: "GET"// HTTP 요청 메소드(GET, POST 등)  
            dataType: "json"// 서버에서 보내줄 데이터의 타입  
            
            success:function(returnMap){
                console.log(returnMap.api);
                console.log(returnMap.dbData);
                listItems(returnMap.dbData);
            },
            
            error:function(jqXHR, status, errorThrown){
                console.log(jqXHR.responseText);
            }
        });
    }
    
    function listItems(listData) {
      $("table > tr").remove();
      var temp, clon;
      var dbData = listData;
      for(var i=0; i<dbData.length; i++) {
          temp = document.getElementsByTagName("template")[0];
          clone = temp.content.cloneNode(true);
          
          td = clone.querySelectorAll("td");
          td[0].textContent = dbData[i].id;
          td[1].textContent = dbData[i].name;
          td[2].textContent = dbData[i].description;
 
          $("table").append(clone);
      }
    }
    
    function doAjax(url){
        //location.href=url;
        var params = $("#form").serialize();
        $.ajax({ 
            url: "${pageContext.request.contextPath}/sample/"+url, // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
            data: params, // HTTP 요청과 함께 서버로 보낼 데이터 
            type: "POST"// HTTP 요청 메소드(GET, POST 등)  
            dataType: "json"// 서버에서 보내줄 데이터의 타입  
            
            success:function(returnMap){
                console.log(returnMap.api);
                ajaxList();
            },
            
            error:function(jqXHR, status, errorThrown){
                console.log(jqXHR.responseText);
                ajaxList();
            }
        });
    }
</script>
 
<form name="form" id="form" style="margin:0px;">
 
<div style="display:inline-block;height:100%;width:100%;">
    <table cellpadding="0" cellspacing="0">
        <tr>
            <th>id</th>
            <th>name</th>
            <th>description</th>
            <th>ref</th>
 
        </tr>
        
<%--     <c:if test="${!empty dbData}">
    <c:forEach var="items" varStatus="status" items="${dbData}">
        <tr>
            <td class="default">${items.id}</td>
            <td class="default">${items.name}</td>
            <td class="default">${items.description}</td>
            <td class="default"></td>
 
        </tr>
    </c:forEach>
    </c:if> --%>
    
    </table>
 
    <div style="float:right;height:50%;width:46%;margin:1.8%;">
        <ul>
            <li>
                <label>id</label><input type="" name="id" placeholder="Id는 자동생성 됩니다" readonly>
            </li>
            <li>
                <label>name</label><input type="" name="name">
            </li>
            <li>
                <label>description</label><input type="" name="description">
            </li>
            <li>
                <label>ref</label><input type="" name="">
            </li>
        </ul>
    </div>
 
</div>
 
</form>
 
<template id="list">
    <tr>
        <td class="default"></td>
        <td class="default"></td>
        <td class="default"></td>
        <td class="default"></td>
    </tr>
</template>
 
<style>
    .default {
        background-color: white
    }
    .hover {
        background-color: lightblue
    }
    .clicked {
        background-color: blue;
        color:white;
    }
</style>
 
<script>
    function r() {
        $('input[name=id]').val($('.clicked:eq(0)').html());
        $('input[name=name]').val($('.clicked:eq(1)').html());
        $('input[name=description]').val($('.clicked:eq(2)').html());
    }
    
    $("#main td").mouseover(function(e){
        $(e.target).parent().children('td').removeClass('default');
        $(e.target).parent().children('td').addClass('hover');
    });
    $("#main td").mouseout(function(e){
        $(e.target).parent().children('td').removeClass('hover');
        $(e.target).parent().children('td').addClass('default');
    });
 
    $(document).click(function(e) {
        if($(e.target).is("#main td")) {
            
            if($(e.target).hasClass('clicked')) {
                $('td').removeClass();
                $('td').addClass('default');
                
            } else {
                $('td').removeClass();
                $('td').addClass('default');
                
                $(e.target).parent().children('td').removeClass();
                $(e.target).parent().children('td').addClass('clicked');
                
                //$('input[name=id]').val($(e.target).siblings(':nth-child(1)').html());
                //$('input[name=name]').val($(e.target).siblings(':nth-child(2)').html());
                //$('input[name=description]').val($(e.target).siblings(':nth-child(3)').html());
            }
        } else {
 
        }
    });
</script>
 
 

 

그리고 내비 부분에 링크를 하나 걸어두겠습니다.

 

nav.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<ul class="hlist">
    <li><a href="javascript:void(0);" onclick="location.href='main.do'">list</a></li>
    <li><a href="">메뉴2</a></li>
    <li><a href="">메뉴3</a></li>
    <li><a href="">메뉴4</a></li>
    <li><a href="">메뉴5</a></li>
</ul>
 
 

 

그리고 서버를 실행하면 다음과 같은 화면이 출력되게 됩니다.

 

C(reate) U(pdate) R(ead) D(elete)

각 기능들을 테스트 해보면서 감을 익혀가시기 바랍니다.

 

그럼 이만.

3탄에 이어 프로젝트의 서비스와 end point를 더 완성해보겠습니다.

 

먼저 text editor를 이용하여 화면단을 제작해보았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
 
    <style type="text/css">
        html, body {
            height:100%;
            margin:0
        }
 
        span, label {
            display:inline-block;margin:6px 25px 8px 25px;
            font-size:18px;
        }
 
        input {
            padding:5px;margin-top:-15px
        }
 
        #head {
            width:100%;height:60px;
            background-color:white;
        }
 
        #head a {
            display: inline-block;margin-left:20px;
            text-decoration: none;color:black;
        }
 
        #head button {
            float: right;margin: 8px 10%;
            font-size: 20px;padding:8px 15px;
            background-color: transparent;
            border:0.1em solid #333;border-radius:8px;
        }
 
        #head button:hover {;
            background-color: #333;
            color:white;
        }
 
        #nav {
            width:100%;
            border-top:1px solid lightgrey;
            background-color: #333;
            text-align:center;
            margin-bottom: -5px;
        }
 
        ul {
          display:inline-block;
          list-style-type:none;
          margin:0;padding:0;overflow:hidden;
          background-color: transparent;
        }
 
        li {
          float: left;
        }
 
        li a {
          display: block;
          text-align: center;
          padding: 14px 26px;
          text-decoration: none;
          color: black;
        }
 
        li a:hover {
 
        }
 
        #nav ul {
        }
 
        #nav li {
        }
 
        #nav li a {
          color: white;
        }
 
        #nav li a:hover {
          background-color: #111;
          border-bottom:0.1em solid blue;
        }
 
        #body{
            width:80%;height:76%;padding:2% 10% 2% 10%;
            background-color:lightgrey;
        }
 
        #main {
            width:95%;height:90%;min-width:800px;
            padding:2% 3%;border-radius:0.5%;
            background-color:white;
        }
 
        #main table {
            display:inline-block;
            background-color:lightgrey;
            border-collapse: collapse;
            font-size: 18px;width:50%;height:58%;
 
        }
 
        #main th {
            padding:10px 24px;
            background-color: grey
        }
 
        #main td {
            padding:10px 24px;
            border-bottom:1px solid grey;
        }
 
        #main ul {
        }
 
        #main li {
        }
 
        #main li a {
            border:1.5px solid grey;
            color:white;
            border-radius:5px;
            margin:8px 8px 0px 8px;
        }
 
        #main li a:hover {
          background-color: grey;
        }
 
        #foot {
            display: table;
            width:96.1%;height:150px;padding:2%;
            background-color:grey;
            color:rgb(32,32,32);
        }
 
        #foot p {
          display: table-cell;
          line-height: 1.5;
          text-align: right;
          vertical-align: bottom;
        }
 
    </style>
</head>
<body>
 
    <div id="head">
        <a href=""><h2>TEST Site</h2></a>
        <button>메뉴6</button>
    </div>
    
    <div id="nav">
        <ul class="hlist">
            <li><a href="">메뉴1</a></li>
            <li><a href="">메뉴2</a></li>
            <li><a href="">메뉴3</a></li>
            <li><a href="">메뉴4</a></li>
            <li><a href="">메뉴5</a></li>
        </ul>
    </div>
 
    <div id="body">
        <div id="main">
 
            <div style="display:inline-block;text-align:right;border-radius:5px 5px 0 0;padding:3px 5px 0px 0;width:100%;background-color:black;">
 
                    <span>hi</span>
                    <ul class="hlist">
                        <li><a href="">C</a></li>
                        <li><a href="">U</a></li>
                        <li><a href="">R</a></li>
                        <li><a href="">D</a></li>
                    </ul>
 
            </div>
 
            <div style="display:inline-block;height:100%;width:100%;">
                <table cellpadding="0" cellspacing="0">
                    <tr>
                        <th>col1</th>
                        <th>col2</th>
                        <th>col3</th>
                        <th>col4</th>
 
                    </tr>
                    <tr>
                        <td class="default">row1</td>
                        <td class="default">rec1</td>
                        <td class="default">prop1</td>
                        <td class="default">data1</td>
 
                    </tr>
                    <tr>
                        <td class="default">row2</td>
                        <td class="default">rec2</td>
                        <td class="default">prop2</td>
                        <td class="default">data2</td>
 
                    </tr>
                    <tr>
                        <td class="default">row3</td>
                        <td class="default">rec3</td>
                        <td class="default">prop3</td>
                        <td class="default">data3</td>
 
                    </tr>
                </table>
 
            <style>
                .default {
                    background-color: white
                }
                .hover {
                    background-color: lightblue
                }
                .clicked {
                    background-color: blue;
                    color:white;
                }
            </style>
 
            <script>
                $("#main td").mouseover(function(e){
                    $(e.target).parent().children('td').removeClass('default');
                    $(e.target).parent().children('td').addClass('hover');
                });
                $("#main td").mouseout(function(e){
                    $(e.target).parent().children('td').removeClass('hover');
                    $(e.target).parent().children('td').addClass('default');
                });
                $("#main td").click(function(e){
 
                    if(!$(e.target).hasClass("clicked")) {
                        $(e.target).parent().children('td').removeClass();
                        $(e.target).parent().children('td').addClass('clicked');
                    } else {
                        $(e.target).parent().children('td').removeClass();
                        $(e.target).parent().children('td').addClass('default');
                    }
                });
            </script>
            
                <div style="float:right;height:50%;width:46%;margin:1.8%;">
                    <ul>
                        <li>
                            <label>col1</label><input type="" name="">
                        </li>
                        <li>
                            <label>col2</label><input type="" name="">
                        </li>
                        <li>
                            <label>col3</label><input type="" name="">
                        </li>
                        <li>
                            <label>col4</label><input type="" name="">
                        </li>
                    </ul>
 
                </div>
 
            </div>
 
        </div>
    </div>
 
    <div id="foot">
        <p>copyright by webman</p>
    </div>
    
</body>
</html>
 

 

이 소스는 다음과 같이 view를 출력합니다.

 

그러면 endpoint 소스를 프로젝트와 연동해보겠습니다.

layout을 나누어 tiles 처리도 진행해보겠습니다.

 

먼저 pom.xml에 maven의 tiles 의존성을 추가하겠습니다.

1
2
3
4
5
6
<!-- Apache Tiles -->
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>3.0.7</version>
</dependency>
 

 

그리고 dispatcher-servlet.xml 에서 하이라이트 부분을 수정하고,

tiles bean을 주입합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        p:viewClass="org.springframework.web.servlet.view.JstlView"
        p:prefix="/WEB-INF/jsp/egovframework/example/" p:suffix=".jsp"/>
        
    <!-- Tiles -->
    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
        <property name="order" value="1"/>
    </bean>
    <bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer" id="tilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/config/egovframework/springmvc/tilesConfig.xml</value>
            </list>
        </property>
    </bean>
 
</beans>
 
 

 

그리고 다음과 같이 구성합니다.

 

custom.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
html, body {
    height:100%;min-width:950px;
    margin:0
}
 
span, label {
    display:inline-block;margin:6px 25px 8px 25px;
    font-size:18px;
}
 
input {
    padding:5px;margin-top:-15px
}
 
#head {
    width:100%;height:60px;
    background-color:white;
}
 
#head a {
    display: inline-block;margin-left:20px;
    text-decoration: none;color:black;
}
 
#head button {
    float: right;margin: 8px 10%;
    font-size: 20px;padding:8px 15px;
    background-color: transparent;
    border:0.1em solid #333;border-radius:8px;
}
 
#head button:hover {;
    background-color: #333;
    color:white;
}
 
#nav {
    width:100%;
    border-top:1px solid lightgrey;
    background-color: #333;
    text-align:center;
    margin-bottom: -5px;
}
 
ul {
  display:inline-block;
  list-style-type:none;
  margin:0;padding:0;overflow:hidden;
  background-color: transparent;
}
 
li {
  float: left;
}
 
li a {
  display: block;
  text-align: center;
  padding: 14px 26px;
  text-decoration: none;
  color: black;
}
 
li a:hover {
 
}
 
#nav ul {
}
 
#nav li {
}
 
#nav li a {
  color: white;
}
 
#nav li a:hover {
  background-color: #111;
  border-bottom:0.1em solid blue;
}
 
#body{
    width:80%;height:76%;padding:2% 10% 2% 10%;
    background-color:lightgrey;
}
 
#main {
    width:95%;height:90%;min-width:800px;
    padding:2% 3%;border-radius:0.5%;
    background-color:white;
}
 
#main table {
    display:inline-block;
    background-color:lightgrey;
    border-collapse: collapse;
    font-size: 18px;width:50%;height:58%;
 
}
 
#main th {
    padding:10px 24px;
    background-color: grey
}
 
#main td {
    padding:10px 24px;
    border-bottom:1px solid grey;
}
 
#main ul {
}
 
#main li {
}
 
#main li a {
    border:1.5px solid grey;
    color:white;
    border-radius:5px;
    margin:8px 8px 0px 8px;
}
 
#main li a:hover {
  background-color: grey;
}
 
#foot {
    display: table;
    width:96.1%;height:150px;padding:2%;
    background-color:grey;
    color:rgb(32,32,32);
}
 
#foot p {
  display: table-cell;
  line-height: 1.5;
  text-align: right;
  vertical-align: bottom;
}
 

 

tilesConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
  "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
 
<tiles-definitions>
 
    <!-- 메뉴 표시 -->
    <definition name="layout" template="/WEB-INF/jsp/egovframework/layout/layout.jsp">
            <put-attribute name="head"   value="/WEB-INF/jsp/egovframework/layout/head.jsp" />
            <put-attribute name="nav" value="/WEB-INF/jsp/egovframework/layout/nav.jsp" />
        <put-attribute name="foot"   value="/WEB-INF/jsp/egovframework/layout/foot.jsp" />
    </definition>
 
    <definition name="*.tiles" extends="layout">
        <put-attribute name="body" value="/WEB-INF/jsp/egovframework/example/{1}.jsp" />
    </definition>
 
     <definition name="*/*.tiles" extends="layout">
         <put-attribute name="body" value="/WEB-INF/jsp/egovframework/example/{1}/{2}.jsp" />
     </definition>
    
</tiles-definitions>
 
s

 

아래부터는 endpoint.html을 layout으로 나누는 작업입니다.

 

layout.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%
  /**
  * 
  */
%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><spring:message code="title.sample" /></title>
    <link type="text/css" rel="stylesheet" href="<c:url value='/css/egovframework/custom.css'/>"/>
 
</head>
<body>
 
    <div id="head">
        <tiles:insertAttribute name="head" />
    </div>
    
    <div id="nav">
        <tiles:insertAttribute name="nav" />
    </div>
 
    <div id="body">
        <div id="main">
            <tiles:insertAttribute name="body" />
        </div>
    </div>
 
    <div id="foot">
        <tiles:insertAttribute name="foot" />
    </div>
    
</body>
</html>
 

 

head.jsp

1
2
3
4
5
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<a href=""><h2>TEST Site</h2></a>
<button>메뉴6</button>
 
 
 

 

nav.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<ul class="hlist">
    <li><a href="">메뉴1</a></li>
    <li><a href="">메뉴2</a></li>
    <li><a href="">메뉴3</a></li>
    <li><a href="">메뉴4</a></li>
    <li><a href="">메뉴5</a></li>
</ul>
 

 

foot.jsp

1
2
3
4
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<p>copyright by webman</p>
 
 
 

 

curd.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
 
<div style="display:inline-block;text-align:right;border-radius:5px 5px 0 0;padding:3px 5px 0px 0;width:100%;background-color:black;">
    <span>hi</span>
    <ul class="hlist">
        <li><a href="">C</a></li>
        <li><a href="">U</a></li>
        <li><a href="">R</a></li>
        <li><a href="">D</a></li>
    </ul>
</div>
 
<div style="display:inline-block;height:100%;width:100%;">
    <table cellpadding="0" cellspacing="0">
        <tr>
            <th>col1</th>
            <th>col2</th>
            <th>col3</th>
            <th>col4</th>
 
        </tr>
        <tr>
            <td class="default">row1</td>
            <td class="default">rec1</td>
            <td class="default">prop1</td>
            <td class="default">data1</td>
 
        </tr>
        <tr>
            <td class="default">row2</td>
            <td class="default">rec2</td>
            <td class="default">prop2</td>
            <td class="default">data2</td>
 
        </tr>
        <tr>
            <td class="default">row3</td>
            <td class="default">rec3</td>
            <td class="default">prop3</td>
            <td class="default">data3</td>
 
        </tr>
    </table>
 
    <div style="float:right;height:50%;width:46%;margin:1.8%;">
        <ul>
            <li>
                <label>col1</label><input type="" name="">
            </li>
            <li>
                <label>col2</label><input type="" name="">
            </li>
            <li>
                <label>col3</label><input type="" name="">
            </li>
            <li>
                <label>col4</label><input type="" name="">
            </li>
        </ul>
    </div>
 
</div>
 
<style>
    .default {
        background-color: white
    }
    .hover {
        background-color: lightblue
    }
    .clicked {
        background-color: blue;
        color:white;
    }
</style>
 
<script>
    $("#main td").mouseover(function(e){
        $(e.target).parent().children('td').removeClass('default');
        $(e.target).parent().children('td').addClass('hover');
    });
    $("#main td").mouseout(function(e){
        $(e.target).parent().children('td').removeClass('hover');
        $(e.target).parent().children('td').addClass('default');
    });
    $("#main td").click(function(e){
 
        if(!$(e.target).hasClass("clicked")) {
            $(e.target).parent().children('td').removeClass();
            $(e.target).parent().children('td').addClass('clicked');
        } else {
            $(e.target).parent().children('td').removeClass();
            $(e.target).parent().children('td').addClass('default');
        }
    });
</script>
 
 
 

 

마지막으로 컨트롤러를 다음과 같이 수정하고

1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/egovSampleList.do")
public String selectSampleList(SampleDefaultVO sampleDefaultVO, 
                               ModelMap modelMap) throws Exception {
    
    sampleDefaultVO = new SampleDefaultVO();
    List<?> dbData = egovSampleService.selectSampleList(sampleDefaultVO);
    modelMap.addAttribute("dbData", dbData);
 
    return "sample/curd.tiles";
}
 
 

 

준비되었다면 서버를 실행하고 다음과 같은 결과를 확인 할 수 있겠습니다.

 

이어서 CURD 연동 작업을 진행해보겠습니다.

그 전에 서비스 소스를 다음과 같이 추가 작성하도록 하겠습니다.

 

EgovSampleServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
 
 
 
 
import javax.annotation.Resource;
 
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
 
 
@Service("sampleService")
public class EgovSampleServiceImpl extends EgovAbstractServiceImpl implements EgovSampleService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(EgovSampleServiceImpl.class);
    
    @Resource(name="sampleMapper")
    private SampleMapper sampleMapper;
 
    /** ID Generation */
    @Resource(name = "egovIdGnrService")
    private EgovIdGnrService egovIdGnrService;
 
    @Override
    public String insertSample(SampleVO vo) throws Exception {
        LOGGER.debug(vo.toString());
 
        /** ID Generation Service */
        String id = egovIdGnrService.getNextStringId();
        vo.setId(id);
        LOGGER.debug(vo.toString());
 
        sampleMapper.insertSample(vo);
        return id;
    }
 
    @Override
    public boolean updateSample(SampleVO vo) throws Exception {
        return sampleMapper.updateSample(vo);
    }
 
    @Override
    public boolean deleteSample(SampleVO vo) throws Exception {
        return sampleMapper.deleteSample(vo);
    }
 
    @Override
    public SampleVO selectSample(SampleVO vo) throws Exception {
        SampleVO resultVO = sampleMapper.selectSample(vo);
        if (resultVO == null)
            throw processException("info.nodata.msg");
        return resultVO;
    }
 
    @Override
    public List<?> selectSampleList(SampleDefaultVO searchVO) throws Exception {
        List<?> result=sampleMapper.selectSampleList(searchVO);
        LOGGER.debug(result.toString());
        return result;
    }
 
    @Override
    public int selectSampleListTotCnt(SampleDefaultVO searchVO) {
        return sampleMapper.selectSampleListTotCnt(searchVO);
    }
}
 
 
 

 

EgovSampleService 인터페이스는 그대로 오버라이딩 해주시고

 

SampleMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
 
 
 
@Mapper("sampleMapper")
public interface SampleMapper {
 
    /**
     * 글을 등록한다.
     * @param vo - 등록할 정보가 담긴 SampleVO
     * @return 등록 결과
     * @exception Exception
     */
    void insertSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 수정한다.
     * @param vo - 수정할 정보가 담긴 SampleVO
     * @return void형
     * @exception Exception
     */
    boolean updateSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 삭제한다.
     * @param vo - 삭제할 정보가 담긴 SampleVO
     * @return void형
     * @exception Exception
     */
    boolean deleteSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 조회한다.
     * @param vo - 조회할 정보가 담긴 SampleVO
     * @return 조회한 글
     * @exception Exception
     */
    SampleVO selectSample(SampleVO vo) throws Exception;
 
    /**
     * 글 목록을 조회한다.
     * @param searchVO - 조회할 정보가 담긴 VO
     * @return 글 목록
     * @exception Exception
     */
    List<?> selectSampleList(SampleDefaultVO searchVO) throws Exception;
 
    /**
     * 글 총 갯수를 조회한다.
     * @param searchVO - 조회할 정보가 담긴 VO
     * @return 글 총 갯수
     * @exception
     */
    int selectSampleListTotCnt(SampleDefaultVO searchVO);
 
}
 
 

 

SampleMapper.xml 에 쿼리를 작성해줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?xml version="1.0" encoding="UTF-8"?>
 
    <select id="selectSampleList" parameterType="searchVO" resultType="egovMap">
 
        SELECT
            ID, NAME, DESCRIPTION, USE_YN, REG_USER
        FROM SAMPLE
        <if test="searchKeyword != null and searchKeyword != ''">
            WHERE 1=1
            <choose>
                <when test="searchCondition == 0">
                    AND    ID LIKE '%' || #{searchKeyword} || '%'
                </when>
                <when test="searchCondition == 1">
                    AND    NAME LIKE '%' || #{searchKeyword} || '%'
                </when>
            </choose>
        </if>
        ORDER BY ID ASC
        <!-- LIMIT #{recordCountPerPage} OFFSET #{firstIndex} -->
    </select>
    
    <insert id="insertSample" parameterType="sampleVO">
        <selectKey resultType="String" order="BEFORE" keyProperty="id">
            SELECT nvl(MAX(id),0)+1 FROM SAMPLE 
        </selectKey>
        <![CDATA[
            INSERT INTO SAMPLE( 
                    ID
                  , NAME
                  , DESCRIPTION
            ) VALUES ( 
                    #{id}
                  , #{name}
                  , #{description}
            )
        ]]>
    </insert>
    
    <update id="updateSample" parameterType="sampleVO">
        <![CDATA[
            UPDATE SAMPLE SET   
                  NAME= #{name}
                , DESCRIPTION= #{description}
            WHERE 
                ID=#{id}
        ]]>
    </update>
    
    <delete id="deleteSample" parameterType="sampleVO">
        <![CDATA[
            DELETE FROM SAMPLE 
            WHERE 
                ID=#{id}
        ]]>
    </delete>
 
</mapper>
 

 

이번에도 컨트롤러를 구현하기 전에 JUnit으로 insert 테스트를 먼저 진행해보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package example;
 
 
import javax.annotation.Resource;
 
import org.springframework.transaction.annotation.Transactional;
 
 
@RunWith(SpringJUnit4ClassRunner.class
@ContextConfiguration(locations ={
        "classpath:/egovframework/spring/context-*.xml",
        "classpath:/egovframework/sqlmap/example/sql-mapper-config.xml",
        "classpath:/egovframework/sqlmap/example/mappers/*Mapper.xml",
}) 
public class BatisTester { 
 
    @Inject
    private DataSource ds;
    
    @Test
    public void dsTest()throws Exception {
       Connection con = ds.getConnection();
       System.out.println("JUnit-dsConnection: " + con);
       con.close();
   }
    
   SampleDefaultVO searchVO; 
   
   @Resource(name="sampleMapper")
   private SampleMapper sampleMapper;
   
   @Resource(name = "sampleService")
   EgovSampleService egovSampleService;
    
   @Before
   public void init() throws Exception {}
   
   @Test
   @Transactional
   @Rollback(true)
   public void dbTest() throws Exception {
       List<?> result=sampleMapper.selectSampleList(searchVO);
       System.out.println("JUnit-dbQuery: " + result);
   } 
   
   @Test
   @Transactional
   @Rollback(true)
   public void create() throws Exception {
    SampleVO sampleVo = new SampleVO();
        sampleVo.setId("id2");
        sampleVo.setName("name2");
        sampleVo.setDescription("des2");
    String result=egovSampleService.insertSample(sampleVo);
   System.out.println("JUnit-create: " + result);
   } 
}
 
 

 

여기까지 하셨다면 DB에 데이터가 삽입되었는지 확인해 봅시다.

테이블에 데이터가 확인 된다면 성공입니다. 

이와 같이 update, select, delete 도 테스트 하여서,

소스와 DB가 잘 연동이 되는지 확인 하시면 되겠습니다. 

 

다음 5탄에서는 이 소스를 이용하여 CURD를 마저 구현해보도록 하겠습니다.

3탄에서는 프로젝트와 Database를 연결하는 실습을 해보겠습니다.

 

이번 실습에서는

프로젝트는 2탄에서 완성한 프로젝트,

Database는 Oracle 11gR2 Express를 사용할 것입니다.

 

1. Database 설치

아래 페이지에서 설치파일을 다운 받아 설치를 진행해주시면 되겠습니다.

https://www.oracle.com/database/technologies/xe-prior-releases.html

 

2. Database 계정 생성 및 권한 부여

Oracle 이 설치되었다면 계정을 생성하고 접근 권한을 부여하자

SQL CLI를 실행하여, 다음과 같이 입력하면 test 계정이 생성됩니다

1
2
3
CONN /AS SYSDBA;
CREATE USER test IDENTIFIED BY test;
GRANT CONNECT, RESOURCE, DBS TO test;
 

 

3. 프로젝트와 Database 연동(connection)

프로젝트와 Database를 연동하기 위해서는 ojdbc 드라이버가 필요합니다.

아래 페이지에서 드라이버를 다운받아

https://www.oracle.com/database/technologies/jdbcdriver-ucp-downloads.html

 

lib 경로에 넣어두겠습니다. 

 

그리고 pom.xml 에 oracle jdbc, test관련 의존성(라이브러리)을 추가합니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        <dependency>
            <groupId>ojdbc</groupId>
            <artifactId>ojdbc</artifactId>
            <version>6</version>
            <scope>system</scope>
            <systemPath>${basedir}/src/main/webapp/WEB-INF/lib/ojdbc6.jar</systemPath>
        </dependency>
        
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.1.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
 
 

 

4. JUnit을 활용한 connection test

이런 과정이 잘 진행되었는지 JUnit을 활용하여 Test를 진행해보겠습니다.

Build Path에 JUnit4 라이브러리도 추가합니다.

 

준비가 되었다면 JUnit 소스로 db와 연결이 잘되는지 테스트를 해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package example;
 
 
 
public class ConnTester { 
    
    private static final String DRIVER ="oracle.jdbc.driver.OracleDriver";
    private static final String URL ="jdbc:oracle:thin:@127.0.0.1:1521:xe"
    private static final String USER ="test";
    private static final String PW ="test";
    
    @Test
    public void testConnect() throws Exception{
        
        Class.forName(DRIVER);
        
        try(Connection con = DriverManager.getConnection(URL, USER, PW)){
            
            System.out.println(con);
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
 
 

 

성공했다면 다음과 같은 결과를 얻을 것입니다.

만약 커넥션 예외가 발생한다면, Build path에서 ojdbc lib가 잘 추가되었는지 확인해봅시다.  

 

5. MyBatis 연결하기

MyBatis 실습을 위한 프로젝트 구성은 다음과 같습니다. 

하이라이트된 부분이 해당 실습의 주요 구성 요소입니다.

우선 xml Config 부터 먼저 살펴보겠습니다.

 

context-datasource.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
       <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:xe"></property>
       <property name="username" value="test"></property>
       <property name="password" value="test"></property>
    </bean>
 
</beans>
 
 

 

context-sqlMap.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
 
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource" />
       <property name="configLocation" value="classpath:/egovframework/sqlmap/example/sql-mapper-config.xml" />
       <property name="mapperLocations">
           <list>
            <value>classpath:/egovframework/sqlmap/example/mappers/*Mapper.xml</value>
        </list>
        </property>
    </bean>
 
</beans>
 
 

 

context-mapper.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
    
        <property name="basePackage" value="egovframework.example"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 
    </bean>
    
</beans>
 
 

 

sample-mapper-config.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
 
<configuration>
    <typeAliases>
        <typeAlias alias="egovMap" type="egovframework.rte.psl.dataaccess.util.EgovMap"/>
        <typeAlias alias="searchVO" type="egovframework.example.sample.service.SampleDefaultVO"/>
        <typeAlias alias="sampleVO" type="egovframework.example.sample.service.SampleVO"/>
        
    </typeAliases>
</configuration>
 

 

SampleMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
 
    <select id="selectSampleList" parameterType="searchVO" resultType="egovMap">
 
        SELECT
            ID, NAME, DESCRIPTION, USE_YN, REG_USER
        FROM SAMPLE
        <if test="searchKeyword != null and searchKeyword != ''">
            WHERE 1=1
            <choose>
                <when test="searchCondition == 0">
                    AND    ID LIKE '%' || #{searchKeyword} || '%'
                </when>
                <when test="searchCondition == 1">
                    AND    NAME LIKE '%' || #{searchKeyword} || '%'
                </when>
            </choose>
        </if>
        ORDER BY ID ASC
        <!-- LIMIT #{recordCountPerPage} OFFSET #{firstIndex} -->
    </select>
 
</mapper>
 
 

 

DTO는 전자정부 sample 프로젝트 기존의 SampleDefaultVO, SampleVO 소스를 사용하면 되며,

다음은 MVC 서비스 소스를 확인해보겠습니다.

 

EgovSampleService.java, 서비스 인터페이스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
 
public interface EgovSampleService {
 
    /**
     * 글을 등록한다.
     * @param vo - 등록할 정보가 담긴 SampleVO
     * @return 등록 결과
     * @exception Exception
     */
    String insertSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 수정한다.
     * @param vo - 수정할 정보가 담긴 SampleVO
     * @return void형
     * @exception Exception
     */
    void updateSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 삭제한다.
     * @param vo - 삭제할 정보가 담긴 SampleVO
     * @return void형
     * @exception Exception
     */
    void deleteSample(SampleVO vo) throws Exception;
 
    /**
     * 글을 조회한다.
     * @param vo - 조회할 정보가 담긴 SampleVO
     * @return 조회한 글
     * @exception Exception
     */
    SampleVO selectSample(SampleVO vo) throws Exception;
 
    /**
     * 글 목록을 조회한다.
     * @param searchVO - 조회할 정보가 담긴 VO
     * @return 글 목록
     * @exception Exception
     */
    List<?> selectSampleList(SampleDefaultVO searchVO) throws Exception;
 
    /**
     * 글 총 갯수를 조회한다.
     * @param searchVO - 조회할 정보가 담긴 VO
     * @return 글 총 갯수
     * @exception
     */
    int selectSampleListTotCnt(SampleDefaultVO searchVO);
 
}
 

 

EgovSampleServiceImpl.java, 서비스 구현부

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 
 
 
 
import javax.annotation.Resource;
 
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
 
 
@Service("sampleService")
public class EgovSampleServiceImpl extends EgovAbstractServiceImpl implements EgovSampleService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(EgovSampleServiceImpl.class);
    
    @Resource(name="sampleMapper")
    private SampleMapper sampleMapper;
 
    /** ID Generation */
    @Resource(name = "egovIdGnrService")
    private EgovIdGnrService egovIdGnrService;
 
    @Override
    public String insertSample(SampleVO vo) throws Exception {
        LOGGER.debug(vo.toString());
 
        /** ID Generation Service */
        String id = egovIdGnrService.getNextStringId();
        vo.setId(id);
        LOGGER.debug(vo.toString());
 
        sampleMapper.insertSample(vo);
        return id;
    }
 
    @Override
    public void updateSample(SampleVO vo) throws Exception {
        sampleMapper.updateSample(vo);
    }
 
    @Override
    public void deleteSample(SampleVO vo) throws Exception {
        sampleMapper.deleteSample(vo);
    }
 
    @Override
    public SampleVO selectSample(SampleVO vo) throws Exception {
        SampleVO resultVO = sampleMapper.selectSample(vo);
        if (resultVO == null)
            throw processException("info.nodata.msg");
        return resultVO;
    }
 
    @Override
    public List<?> selectSampleList(SampleDefaultVO searchVO) throws Exception {
        List<?> result=sampleMapper.selectSampleList(searchVO);
        LOGGER.debug(result.toString());
        return result;
    }
 
    @Override
    public int selectSampleListTotCnt(SampleDefaultVO searchVO) {
        return sampleMapper.selectSampleListTotCnt(searchVO);
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

SampleMapper.java, myBatis 매퍼인터페이스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
 
 
 
@Mapper("sampleMapper")
public interface SampleMapper {
 
    void insertSample(SampleVO vo) throws Exception;
 
    void updateSample(SampleVO vo) throws Exception;
 
    void deleteSample(SampleVO vo) throws Exception;
 
    SampleVO selectSample(SampleVO vo) throws Exception;
 
    List<?> selectSampleList(SampleDefaultVO searchVO) throws Exception;
 
    int selectSampleListTotCnt(SampleDefaultVO searchVO);
 
}
 

 

이하 소스가 준비되었다면, JUnit으로 잘 동작하는지 Test를 진행해보겠습니다.

 

BatisTester.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package example;
 
 
import javax.annotation.Resource;
 
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
 
 
@RunWith(SpringJUnit4ClassRunner.class
@ContextConfiguration(locations ={
        "classpath:/egovframework/spring/context-*.xml",
        "classpath:/egovframework/sqlmap/example/sql-mapper-config.xml",
        "classpath:/egovframework/sqlmap/example/mappers/*Mapper.xml",
}) 
public class BatisTester { 
    
    private static final Logger LOGGER = LoggerFactory.getLogger(BatisTester.class);
    
    @Inject
    private DataSource ds;
    
    @Test
    public void dsTest()throws Exception {
       Connection con = ds.getConnection();
       System.out.println("JUnit-dsConnection: " + con);
       con.close();
   }
    
   SampleDefaultVO searchVO; 
   
   @Resource(name="sampleMapper")
   private SampleMapper sampleMapper;
    
   @Before
   public void init() throws Exception {}
   
   @Test
   @Transactional
   @Rollback(true)
   public void dbTest() throws Exception {
       List<?> result=sampleMapper.selectSampleList(searchVO);
       System.out.println("JUnit-dbQuery: " + result);
   } 
}
 
 

 

성공했다면 다음과 같은 결과를 얻을 것입니다.

 

그러면 실제 서버에 적용해보기 위해 Controller와 view를 추가해보겠습니다.

 

EgovSampleController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
 
import javax.annotation.Resource;
 
import org.springframework.stereotype.Controller;
 
 
@Controller
public class EgovSampleController {
    
    @Resource(name = "sampleService")
    EgovSampleService egovSampleService;
 
    @RequestMapping(value = "/egovSampleList.do")
    public String selectSampleList(SampleDefaultVO sampleDefaultVO, 
                                   ModelMap modelMap) throws Exception {
        
        sampleDefaultVO = new SampleDefaultVO();
        List<?> dbData = egovSampleService.selectSampleList(sampleDefaultVO);
        modelMap.addAttribute("dbData", dbData);
 
        return "sample/egovSampleList";
    }
}
 
 

 

webapp/WEB-INF/jsp/egovframework/example/sample/

egovSampleList.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><spring:message code="title.sample" /></title>
    <link type="text/css" rel="stylesheet" href="<c:url value='/css/egovframework/sample.css'/>"/>
    <script type="text/javaScript" language="javascript" defer="defer">
 
    </script>
</head>
 
<body style="text-align:center; margin:0 auto; display:inline; padding-top:100px;">
 
리스트
${dbData}
 
</body>
</html>
 
cs

 

여기까지 하셨다면 Ctrl + F11 하여 출력 결과를 알아보겠습니다.

아래와 같이 출력되었다면 오늘 실습은 성공적으로 마치셨습니다. 수고하셨습니다~

 

다음 4탄에서는 end point에서 CURD 가 가능하도록 프로젝트를 완성해보겠습니다.

 

 

 

 

1탄까지 따라오셨다면 프로젝트가 다음과 같이 구성되어 있을겁니다.

Example인 만큼 많은 샘플 소스로 구성되어 있습니다.

 

 

그러나 학습을 위해 중요한 부분만 남기고 하나씩 알아보겠습니다.

그리고 다음과 같이 프로젝트를 커스터마이징을 하였습니다.

 

변경된 부분은 다음과 같습니다.

(https://colorscripter.com/ 사용하여 코드를 작성하였습니다)

 

dispatcher-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
 
    <context:component-scan base-package="egovframework">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
 
        <property name="defaultErrorView" value="cmmn/egovError"/>
        <property name="exceptionMappings">
            <props>
                <prop key="org.springframework.dao.DataAccessException">cmmn/dataAccessFailure</prop>
                <prop key="org.springframework.transaction.TransactionException">cmmn/transactionFailure</prop>
                <prop key="egovframework.rte.fdl.cmmn.exception.EgovBizException">cmmn/egovError</prop>
                <prop key="org.springframework.security.AccessDeniedException">cmmn/egovError</prop>
            </props>
        </property>
    </bean>
 
        p:viewClass="org.springframework.web.servlet.view.JstlView"
        p:prefix="/WEB-INF/jsp/egovframework/example/" p:suffix=".jsp"/>
 
</beans>
 
 

EgovSampleController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
import org.springframework.stereotype.Controller;
 
@Controller
public class EgovSampleController {
 
    @RequestMapping(value = "/egovSampleList.do")
    public String selectSampleList() throws Exception {
 
        return "sample/egovSampleList";
    }
}
 
 
 

 

egovSampleList.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><spring:message code="title.sample" /></title>
    <link type="text/css" rel="stylesheet" href="<c:url value='/css/egovframework/sample.css'/>"/>
    <script type="text/javaScript" language="javascript" defer="defer">
 
    </script>
</head>
 
<body style="text-align:center; margin:0 auto; display:inline; padding-top:100px;">
 
리스트
 
</body>
</html>
 
 
 

 

그리고 서버를 실행하면

 

다음과 같이 페이지가 출력됩니다.

 

이 는 다음과 같은 과정에 의하여 출력되는 것입니다.

 

1) web.xml 에 정의된, DispatcherServlet 과 welcome-file-list(방문 첫 화면 정의)

 

2) dispatcher-servlet.xml 에 정의된, ViewResolver과 어노테이션 컴포넌트 스캔 

 

3) EgovSampleController에 정의된, Controller 구현부

 

4) index.jsp에서 리다이렉트

 

5) egovSampleList.jsp의 EndPoint 소스

 

다음 탄에서는 DB 연동을 진행해보도록 하겠습니다.

전자정부 기반의 스프링 레거시 프로젝트를 아래의 방법으로 순서대로 생성해보겠습니다.

 

1. 개발 환경세팅

 

- jdk, tomcat server는 기본적으로 설치되어 있어야하며,

 이클립스 툴 설치 후 build path 및 lib import 설정해주어야 합니다.

 버전은 jdk1.8, tomcat7.0 을 권장드립니다.

 

- 전자정부 프레임워크(이클립스) 설치

https://www.egovframe.go.kr/EgovDevEnvRelease_300.jsp?menu=3&submenu=2&leftsub=2#

3.8버전이 가장 최신 버전이나,

요구 사양이 높은 Eclipse Oxygen 엔진을 사용하기 때문에,

컴퓨터 사양이 좋지 않다면 비교적 요구 사양이 낮은 Eclipse Mars 엔진을 사용하는 3.6버전이

개발하기는 덜 무거운 느낌을 받으실 것입니다. 

이 포스팅에서는 3.6버전을 사용하겠습니다.

 

2. 프로젝트 생성

 

- eGovFrame Web 프로젝트 생성

 

 

2-1. Generate Example 체크 안하는 경우

 

프로젝트를 생성하고 

 

그리고 서버를 실행하면...

 

404 에러가 발생한다.

 

그러나

2-2. Generate Example 체크 하는 경우

 

프로젝트를 생성하고

 

에러가 아닌, 게시판 화면이 출력된다.

 

이런 차이점이 발생하는건 Example 프로젝트에 기본 설정들이 구성되어 있기 때문이다.

 

 

그럼 2탄에서 조금 더 자세히 들여다 보자.

 

 

'웹개발 > 개론' 카테고리의 다른 글

프로그래밍 맛보기(2부)  (0) 2021.07.15
프로그래밍 맛보기(1부)  (1) 2021.07.13
개발(프로그래밍)에 대하여  (0) 2019.12.21
server-side 언어중 JAVA에 대하여  (0) 2019.12.21
웹 개발 입문하기전에...  (0) 2019.12.21

개발은 한편의 시나리오이다.

어떤 컴포넌트들을, 또 그들에 대해 어떤 시나리오를 쓸것인가?

예외처리는 그 시나리오에서 벗어나는 상황을 처리하는 다른 시나리오이다. 

 

즉 개발을 한다는 것은 시나리오를 작성하는 행위의 연속이다.

그래서 생각의 연속이며, 고뇌의 연속이다.

또한 생각을 구체화/구현하는데 많은 연구와 수고가 필요하다.

그것을 수행하기 위해 보통 앉아서 보내는 시간이 상당히 많다.

일과 일상을 넘어서 쉴새없이 생각해야할때도 있다.

 

개발은 이런 것이므로 호불호가 강할 수 있는 분야라 할 수 있겠다.

'웹개발 > 개론' 카테고리의 다른 글

프로그래밍 맛보기(2부)  (0) 2021.07.15
프로그래밍 맛보기(1부)  (1) 2021.07.13
JAVA 기본 문법  (0) 2019.12.21
server-side 언어중 JAVA에 대하여  (0) 2019.12.21
웹 개발 입문하기전에...  (0) 2019.12.21

웹 개발에서 server-side 언어는 다양하다. php, Python 등.

그런데 왜 많고 많은 언어중에 JAVA가 많이 쓰일까?

그 이유는 세가지 정도로 생각해본다.

 

1. 모듈단위 개발에 적합하고 재사용성이 좋다.

2. 라이브러리 및 레퍼런스가 풍부하다. https://docs.oracle.com/javase/7/docs/api/ 참조

3. STS 등 개발 툴이 막강하다.

 

범용적인 언어이고 역사도 역사인 만큼 사용 편의성이 많이 발전 되어온 개발 언어이다. 

JAVA의 역사를 거슬러 올라간다면...

1995년 썬시스템즈의 제임스 고슬링이 하드웨어 호환성이 좋은 언어를 개발하면서 JAVA를 탄생시켰다.

JAVA의 특징으로는 JVM(Java Virtual Machine)을 통해서 구동이 되며,

JVM은 linux, windows 등 다양한 운영체제에서 설치가 가능하다.

 

JVM은 아래와 같은 형태로 구성이 되어있다.

[참조] server-side 언어중 JAVA에 대하여[https://wbman.tistory.com/]. (2019.12.21). URL: http://edu.copykiller.com/edu-source/faq/?mod=document&uid=156

 

그리고 JAVA는 네가지의 에디션이 있다.

Java SE(Java Standard Edition / J2SE)

Java EE(Java Enterprise Edition / J2EE)

Java ME(Java Micro Edition / J2ME)

JavaFX

 

이중에서 웹개발은 이중에서 Java EE을 사용하게 된다. 

 

자바언어의 큰 특징은 객체지향언어이며, 강타입 언어(strictly typed language)이다.

즉 개발을 진행할때 타입을 명확히 선언해주어야 하는 부분이 있다. 그렇지 않으면 에러가 발생한다. 

그 점을 꼭 주의하고 개발해야한다.

JAVA로 개발하면서 정말 많이 접하게 되는 NumberFormatException이 이런 자바의 특성을 말해준다.

 

'웹개발 > 개론' 카테고리의 다른 글

프로그래밍 맛보기(2부)  (0) 2021.07.15
프로그래밍 맛보기(1부)  (1) 2021.07.13
JAVA 기본 문법  (0) 2019.12.21
개발(프로그래밍)에 대하여  (0) 2019.12.21
웹 개발 입문하기전에...  (0) 2019.12.21

문서를 작성하려면 워드프로세서가 필요하듯이,

client-side 개발에 앞서 우리는 텍스트에디터가 필요합니다.

물론 메모장으로 작성하고 확장자를 .txt가 아닌 .html로 바꾸는 형태로 진행할 수도 있습니다.

그러나 편의성을 위해서 텍스트에디터를 다운받아 설치하고 진행해봅시다.

 

텍스트에디터는 다양하지만, 저는 서브라임텍스트를 이용해서 개발을 진행하겠습니다.

https://www.sublimetext.com/ 에서 다운받으시고 설치하시면 됩니다.

설치 후에는 간단한 설정 몇가지 진행하겠습니다.

 

1. 결과물을 브라우저로 출력해주는 단축키를 설정하는 것.

 http://bitly.kr/Zq14clZ 를 참고하셔서 진행하시면 되겠습니다.

 

2. Emmet이라는 강력한 자동완성plugin을 설치하는 것.

 https://webdir.tistory.com/399 을 참고해주세요. 

 

브라우저는 Chrome으로 사용하겠습니다.

크롬이 없으시다면 https://www.google.co.kr/chrome/ 에서 다운받으시고 설치하시면 됩니다.

 

설치하셨다면 초기화면이 보이실겁니다. 

우측 아래 Plain Text를 눌러주시고 HTML를 찾아 바꾸어 줍시다.

 

다 되셨다면 위와 같이 작성하여 주시고 tab키를 눌러주시면...

아래와 같이 html문서 기본양식이 자동생성 됩니다.

 

여기서 <!DOCTYPE html>는 해당 문서를 HTML 형식임을 선언하는 부분입니다.

<html>은 HTML 문서의 시작을 알리는 부분이며,

</html>는 HTML 문서의 끝을 정의해줍니다. 

이 꺾쇠(<)로 구성된 요소를 태그라 하며 또한 DOM 엘리먼트(The Document Object Model)라고도 부릅니다. 

즉 HTML 문서에서 미리 선언되어 있는 HTML 언어의 문법입니다.

 

파일을 저장하고 Ctrl+b를 누르면 위와 같이 브라우저에서 백지로 출력됩니다. 

html파일에 아무것도 작성하지 않았기 때문입니다.

그러나 아래와 같이 <body></body>부분에 문서 내용을 작성하고 다시 단축키를 눌러보시면

아래와 같이 다음과 같은 button 요소가 출력됨을 확인 할 수 있습니다.

이것을 CSS 효과를 주어 다른 형태로 꾸밀수도 있습니다. 한번 해보겠습니다.

요소가 더 세련되게 변한것 같습니다.

마지막으로 JavaScript를 활용하여 서비스는 아니지만 알람을 띄우는 간단한 동작을 구현해보겠습니다.

버튼을 누르면 alert이 발생합니다.

if 문법을 응용해 입력하는 값이 숫자인지 체크 할수도 있겠습니다.

client-side 맛보기는 여기까지 하겠습니다. 

개인용 PC, 노트북, 스마트폰이 등장하면서

컴퓨터와 인터넷은 우리 생활에서 빠질 수 없는 컨텐츠가 되었습니다.

그 중에서도 거대한 컴퓨터 통신망인 인터넷을 활용할 수 있게 해주는 웹 브라우저를 많이 쓰실겁니다.

우리는 인터넷 익스플로러나 크롬과 같은 웹 브라우저를 이용하여 구글과 같이 검색엔진을 지원하는 포털 사이트에 방문하여 다양한 컨텐츠들을 검색 할수 있고, 이를 통해 쇼핑몰의 홈페이지를 찾고 방문하여 쇼핑몰의 물건들을 검색하고, 회원 가입을 하고, 물건을 구매 할 수도 있습니다. 그리고 이 블로그에 방문하시게 된것도 '웹 개발이란 것이 무엇인가?'라는 호기심에 키워드를 검색하여 방문하게 되셨을 겁니다.

 

여기까지 정리해볼때 인터넷은 거대한 컴퓨터 통신망이고,

우리는 그 통신망을 이용하여 서로의 필요를 찾고 제공하고 있습니다.

그리고 웹 브라우저라는 프로그램을 통해 그 통신망을 이용하고 있습니다.

 

브라우저(browser)는 검색기라는 의미입니다. 그렇다면 웹이란 무엇일까요.

웹은 1980년 팀버너스리 라는 사람이 구상한 Hypertext 중심 정보 관리 시스템입니다.

그리고 각 Hypertext들은 HTML(Hypertext Mark-up Language)으로 작성됩니다.

 

HTML은 하나의 컴퓨터 언어이지만 작성된 코드는 웹 브라우저를 통하여

사용자에게 제작자가 의도한 화면을 출력하게 됩니다.

그렇다면 HTML만으로 웹 개발이 진행될까요?

 

사실 HTML만으로 웹개발을 하기에는 한계가 있습니다.

로그인과 같이 우리가 흔히 클릭하고 동작하는 기능들은 HTML만으로는 구현할 수 없기 때문입니다.

이런 동작적인 부분을 구현하기 위해서는 WAS와 대표적으로 JAVA와 같은 서버언어가 필요합니다.

또 필요에 따라 데이터베이스가 필요하기도 합니다.

 

즉 웹 개발을 하기 위해선 우리는 몇가지 언어와 툴들을 알아야 합니다.

하나 하나 짚어 드리기 전에 우선 웹 페이지의 동작 과정을 말씀드리겠습니다.

 

1. 사용자 화면(client):

사용자는 화면의 검색바 또는 url을 통해 제공자에게 서비스를 요청(request)한다.

2. WAS는 그 요청을 제공자에게 전달한다. 

3. 제공자 즉 서버(server)는 서비스를 반환하여 그 요청에 응답(response)한다.

 

여기서 확인 할 수 있는건 제공자에게 서비스를 요청할 수 있는 사용자 화면이 존재하며,

제공자 측에서 요청에 따라 서비스를 처리 한다는 것입니다.

그 사용자 화면을 클라이언트단(client-side),

서비스를 제공하는 로직 부분을 서버단(server-side)이라 부릅니다.

 

클라이언트단을 개발하는데 필요한 언어로는

HTML, CSS, Ajax 등 클라이언트 언어가 필요하며

 

서버단을 개발하는데 필요한 언어로는 

JAVA 또는 Python 등 서버 언어,

부가적으로 데이터베이스 및 데이터베이스 언어 SQL이 필요합니다.

 

여기까지 참고하시고 개발을 시작해봅시다.

'웹개발 > 개론' 카테고리의 다른 글

프로그래밍 맛보기(2부)  (0) 2021.07.15
프로그래밍 맛보기(1부)  (1) 2021.07.13
JAVA 기본 문법  (0) 2019.12.21
개발(프로그래밍)에 대하여  (0) 2019.12.21
server-side 언어중 JAVA에 대하여  (0) 2019.12.21

+ Recent posts