SQL Database와 NoSQL의 차이점은 아래의 table과 같지만 가장 중요한 점은 데이터 저장 방법이 table 형태인지, JSON type "Key-Value" 형태인지 여부이다.

 

Differences between SQL and NoSQL

The table below summarizes the main differences between SQL and NoSQL databases.

SQL DatabasesNoSQL Databases

Data Storage Model Tables with fixed rows and columns Document: JSON documents, Key-value: key-value pairs, Wide-column: tables with rows and dynamic columns, Graph: nodes and edges
Development History Developed in the 1970s with a focus on reducing data duplication Developed in the late 2000s with a focus on scaling and allowing for rapid application change driven by agile and DevOps practices.
Examples Oracle, MySQL, Microsoft SQL Server, and PostgreSQL Document: MongoDB and CouchDB, Key-value: Redis and DynamoDB, Wide-column: Cassandra and HBase, Graph: Neo4j and Amazon Neptune
Primary Purpose General purpose Document: general purpose, Key-value: large amounts of data with simple lookup queries, Wide-column: large amounts of data with predictable query patterns, Graph: analyzing and traversing relationships between connected data
Schemas Rigid Flexible
Scaling Vertical (scale-up with a larger server) Horizontal (scale-out across commodity servers)
Multi-Record ACID Transactions Supported Most do not support multi-record ACID transactions. However, some—like MongoDB—do.
Joins Typically required Typically not required
Data to Object Mapping Requires ORM (object-relational mapping) Many do not require ORMs. MongoDB documents map directly to data structures in most popular programming languages.

 

Mongo start Commands

  Start command Termination command
Server #mongod Ctrl + c
Shell #mongo Ctrol + c

 

Mongo Shell CRUD Commands(생성, 조회, 업데이트, 삭제)

Shell Commands Remark
db.foods.insert({name: "Kimchi", "origin: "Korea", isFav: true}) foods에 name과 origin, isFav이란 Key에 해당하는 value들을 각각 저장
db.foods.find() foods에 저장된 모든 값들을 출력한다
db.foods.find({name: "sushi"}) foods에 저장된 데이터 중 Key-Value가 각각 name, sushi인 데이터를 모두 보여준다.
db.foods.update({name: "Kimch"}, {name: "Bulgogi"}) name이 Kimch인 데이터를 {name: "Bulgogi"}라는 값으로 update한다. 기존의 3개 Key-Value가 사라지고, 오직 name: Bulgogi라는 1개의 Key-Value 값만 저장된다.
db.foods.update({name: "Kimch"}, {$set: {name: "Bulgogi"}}) name이 Kimch인 데이터에서 name만 Bugogi로 업데이트한다
db.foods.remove({name: "Bulgogi"}) name이 "Bulgogi"인 데이터를 모두 삭제한다.

 

UNIX/Linux 시스템에서 사용자 계정으로 java 컴파일이 발생한 경우 다음과 같은 에러가 발생한다. 이는 컴파일이나 라이브러리를 구동할 때 사용하는 임시파일을 쓸 권한이 없어 발생하는 문제이다. 특히 root권한을 가진 계정으로 구동 시에 문제가 없을 경우는 100% 권한으로 인한 문제이다. 다만 매번 root 계정을 사용할 수 없으므로, 해당 권한이 필요한 디렉토리가 어디인지 확인하고 시스템/사용자 계정에 쓰기권한을 부여해야 한다.

 

 

 

실제 발생했던 에러와 테스트를 위해 구현한 에러 메세지는 조금 다른데, 핵심은 아래와 같다. 

 

java.io.IOException : Permision denied

at java.io.UnixFileSystem.createFileExclusively(Native Method)

at java.io.File.createTempFile

 

 

본 case의 경우 /tmp 내에 2개의 디렉토리가 생성되는데, 각각의 디렉토리가 생성되는 이유는 아래와 같다.

[그림 1] /tmp 디렉토리 내에 umsadm이라는 계정으로 생성된 디렉토리가 2개 보인다.

 

1.   Hsperfdata 디렉토리 및 내부의 파일은 JVM에서 모니터링을 수행하기 위해 생성된다.

 

2.   poifiles 디렉토리는 Apache POI API를 사용할 경우 /tmp에 생성된다. Apache POI API의 역할은 다양한 포맷의 문서파일에서 text를 추출하는 역할을 수행한다. (https://poi.apache.org/text-extraction.html)

 

 

Solution

JVM Apache POI API가 정상적으로 /tmp 디렉토리에서 쓰기작업을 할 수 있도록 해당 계정에 쓰기 권한을 부여한다. 

이번 클래스는 페이지에서 적용할 내용은 다음과 같다. 각 페이지별로 적용 할 수도 있지만 DRY(Don't Repeate Youself)를 적용하기 위해 공통부분을 header.ejs에 적용한다. 

 

1. Contents를 중앙에 위치 시키기 

2. 각 페이지 상단에 navbar 추가 및 Login, Sign Up, Logout button 적용

3. <input> 박스를 좌우가 아닌 상하로 위치시키고, 버튼 디자인 적용

 

 

[그림 1] Navbar class로 디자인 적용 전(좌) 후(우) 

 

1. Contents를 중앙에 적용

기존 news.ejs 코드 
<%- include("partials/header")%>

<h1>Create a New Campground</h1>
<form action="/campgrounds" method="POST">
	<input type="text" name="name" placeholder="name">
	<input type="text" name="image" placeholder="image">
	<button>Submit!</button>
</form>

<a href="/campgrounds">Go back</a>
<%- include("partials/footer")%>



아래와 같이 변경한다. 

<%- include("partials/header")%>
<div class="container">		<!-- 모든 컨텐츠를 중앙에 위치시키도록 container class를 이용 -->
	<div class="row"> 		<!-- input 박스를 좌우가 아닌 상하로 표시하기 위해 row class 적용 -->
		<h1 style="text-align: center;">Create a New Campground</h1> <!-- text가 중앙에 출력되도록 text-align 적용 -->
		<div style="width: 30%; margin: 25px auto;"> <!-- input box의 크기를 30%로 적용하고, form 태그를 감싸는 div 상하에 25px, 좌우에 auto로 margin을 적용 -->
			<form action="/campgrounds" method="POST">
				<div class="form-group">			<!-- 각 input 태그 상하에 여유공간을 주기 위해 개별로 form-group 설정-->
					<input class="form-control" type="text" name="name" placeholder="name">
				</div>				
				<div class="form-group">
					<input class="form-control" type="text" name="image" placeholder="image">	
				</div>
				<div class="form-group">				
					<button class="btn btn-lg btn-primary btn-block">Submit!</button> <!-- 버튼 사이즈를 증가시키고, 색상 변경--> 
				</div>
			</form>
			<a href="/campgrounds">Go back</a> <!-- a 태그 또한 form div에 적용시켜 input 태그와 크기를 일치화시킨다.-->
		</div>
	</div>

</div>
<%- include("partials/footer")%>

[그림 2] css 디자인을 적용한 new.ejs

 

 

2. 상단 메뉴바를 디자인 한 후 모든 페이지에 적용될 수 있도록 header.ejs에 적용

 

 

partials/header.ejs

<!DOCTYPE html>
<html>
	<head>
		<title>Yelpcamp</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
	</head>
	<body>



<!-- 아래와 같이 적용시킨다.-->
        
<!DOCTYPE html>
<html>
	<head>
		<title>Yelpcamp</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
	</head>
	<body>
	<nav class="navbar navbar-default"> <!-- 페이지 최상단에 메뉴를 디자인 하기 위해 navbar-default 적용 -->
		<div class="container-fluid">    <!-- 텍스트가 들어갈 navbar-->
			<div class="navbar-header">	<!-- 헤더 생성, 페이지 이름이나 회사 이름을 적용시킬 때 사용-->
				<a class="navbar-brand" href="/">YelpCamp</a>  <!-- 브랜드를 강조하기 위한 클래스-->
			</div>
			<div class="collapse navbar-collapse">	<!-- 반응형으로 페이지 크기가 작아질 때 햄버거 메뉴 적용을 위해 추가 -->
				<ul class="nav navbar-nav navbar-right">  <!-- li태그를 상하가 아닌 좌우로 위치시기 위해 적용 -->
					<li><a href="/">Login</a></li>
					<li><a href="/">Sign Up</a></li>
					<li><a href="/">Logout</a></li>
				</ul>
			</div>
		</div>
	</nav>
		

# 햄버거 메뉴는 구분만 해놓고 다음 강의에서 적용

 

 

[그림 3[ header.ejs에 css 미적용(상) 적용(하) 

 

이번 Lecture에서 적용할 내용은 아래와 같다. 

1. Bootstrap 3.3.5 적용

2. Semantic web에서 사용하는 <header> 활용하기 

3. div를 활용하여 컨텐츠를 중앙에 위치 시키기

 

[그림 1] BootStrap 미적용(좌) / BootStrap 적용(우)

 

 

1. BootSrtap의 <div class="container">  contents 를 가운데 정렬

현재 campground.ejs는 프레임만 설정되어 있는 상태이며, 실제 브라우저에서 출력되는 이미지는 [그림 1]의 좌측 사진이다.

campground.ejs

<%- include("partials/header") %>

<h1>This is the campgrounds page!</h1>

<a href="/campgrounds/new">Add New Campground</a>
<% campgrounds.forEach((campground) =>{ %>
	<div>
		<h4>
			<%= campground.name %>	
			<img src = "<%= campground.image %>">
		</h4>
	</div>	
<%}) %>

<%- include("partials/footer") %>


<!-- 1 ~ 15 라인의 코드를 아래와 같이 <div class="container>로 묶어준다 -->


<%- include("partials/header") %>

<div class="container">
<h1>This is the campgrounds page!</h1>

<a href="/campgrounds/new">Add New Campground</a>
<% campgrounds.forEach((campground) =>{ %>
	<div>
		<h4>
			<%= campground.name %>	
			<img src = "<%= campground.image %>">
		</h4>
	</div>	
<%}) %>
</div>
<%- include("partials/footer") %>

[그림 2] <div class="container"> 로 묶어주기 전(좌) 후(우)

 

2. jumbotron을 활용하여 header 적용, Button 디자인과 화면에 출력될 이미지 갯수를 반응형으로 적용

2-1. jumbotron은 Bootstrp에서 자주 쓰이는 디자인 중 하나이며, 유저가 페이지에 접속했을 경우 이목을 끌기 위해 사용한다. 

2-2. <div class="col-md-3 col-sm-6"> 

: div는 화면을 12분할로 계산하며, 해당 클래스를 적용할 경우 중간 해상도에서 개별 사이즈는 3/12(즉 총 이미지 숫자는 4개), 작은 해상도에서는 개별로 6/12(총 2개)가 출력된다. [그림 1]에서 볼 수 있듯이 이미지는 4개가 출력된다. 아래

 

 

<%- include("partials/header") %>
<div class="container">
<!--모든 태그를 container에 넣어 가운데로 정렬시킴(좌우 여백을 줌) -->
	<header class="jumbotron">
<!--div와 비슷한 역할을 함. 차이점은 semantic web 의미 분석 시 사용 -->
		<div class="container">
<!-- 헤더와 버튼을 디자인하기 위해 따로 묶어준다-->
          <h1>Welcome To YelpCamp!</h1>
          <p>View our hand-picked campgrounds from all over the world</p>
          <p>
              <a class="btn btn-primary btn-lg" href="/campgrounds/new">Add New Campground</a>
          </p>		<!-- Button 디자인 및 크기 조절--> 
		</div>
	</header>


	<!-- thumnail 높이를 조절하여 최대한 센터에 맞춘다-->
	<div class="row text-center" style="display:flex; flex-wrap: wrap;">
		<% campgrounds.forEach((campground) =>{ %>
			<div class="col-md-3 col-sm-6">					<!-- 반응형으로 스크린 사이즈에 따라 한 줄에 출력될 이미지 개수를 지정한다.  -->
				<div class="thumbnail"> 					<!-- thumnail을 사용한다. 이미지는 가로를 100%로 잡았을 경우  -->
					<img src = "<%= campground.image %>">
					<div class="caption">
						<h4><%= campground.name%></h4>
					</div>
				</div>
			</div>	
		<%}); %>
	</div>
</div>

<%- include("partials/footer") %>

 

 

3. div를 이용한 디자인이 적용된 페이지

[그림 3] thumbnail이 적용된 페이지

Note about YelpCamp: Styling Campgrounds Lecture

 

Hi Everyone,
It has been brought to my attention that in the next lecture there's a slight editing fluke.
 
At 5:11 Colt adds a row div with an h3 that says: "Our Most Popular Campgrounds", but when he "heads back over" at 5:19 it's gone.

This isn't anything major, but I created this note to alleviate any potential confusion caused by the glitch.

You can leave the h3 element in there or remove it if you like.

Also, at around 3 minutes and 40 seconds into the video Colt uses the class name btn-large to style the bootstrap button. The class name should actually be: btn-lg

Also, be sure to use bootstrap 3.3.x as version 4 has syntax differences and using it will break your project's layout. If you want to migrate from version 3 to 4 after completing the app then checkout this video.

Thanks,
Ian
Course TA

이번 강의에서 진행할 내용은 크게 아래와 같다.

1. 클라이언트로부터 POST로 요청받은 데이터를 parsing 하기 위한 body-parser 설치

2. /campground에 새로운 name와 imageUrl을 추가하기 위한 페이지 생성

3. 전달받은 name과 imageUrl를 newCampground object로 저장한 후 campground 배열에 저장(push)

4. /campground에 전달받은 name과 imageUrl을 출력

 

 

1. body-parser 설치 및 호출

1-1. body-parser란? POST method로 파라미터가 서버로 전송되었을 경우, 해당 파라미터를 추출하기 위한 패키지

[그림1] 서버에 body-parser 설치

1-2. app.js에서 bodyParser 호출

const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended: true}));

body-parser에 대한 설명은 github 링크 참조

 

 

1-3. 새로운 route 추가 및 post 동작 테스트

app.post("/campgrounds", (req, res) => {    //express 서버에서 '/campgrounds'에 'POST'가 요청될 경우 
	res.send("YOU HIT THE POST ROUTE");		// "YOU HIT.." 라는 데이터를 클라이언트로 response한다.
});

app.get("/campgrounds/new" (req, res) => { //	'/campegrounds/new'로 GET이 요청될 경우 
	res.render("new")					   //   YelpCamp/v1/view 아래에서 new라는 이름의 파일을 찾아 렌더링하여 응답한다. 
});

 

 

[그림 2]Postman으로 GET method Request

 

 

 

[그림 3] Postman으로 POST method Request

 

 

2. 데이터를 전달받을 ejs 생성

<%- include("partials/header")%>		<!--lesson 296 참조-->

<h1>Create a New Campground</h1>
  <form action="/campgrounds" method="POST">			<!--아래의 input 값을 POST method로 /campgrounds에게 request-->
    <input type="text" name="name" placeholder="Insert name">		<!--입력될 데이터는 name이란 이름 속성을 가지고 있음-->
    <input type="text" name="image" placeholder="Insert imageUrl">  <!--입력될 데이터는 image이란 이름 속성을 가지고 있음--> 
    <button>Submit!</button>							<!--해당 버튼을 누르면 데이터가 전송됨-->
  </form>
<a href="/campgrounds">Go back</a>
<%- include("partials/footer"%>		<!--lesson 296 참조-->

[그림 4] new.ejs 페이지(/campgrounds/new 라는 주소를 가지고 있음 "1-3" 참조)

 

 

3. POST로 전달된 파라미터를 저장하여 /campgrounds에서 출력

3-1. /campgrounds/new로 받을 데이터를 저장하기 위한 array 설정

기존에 사용하고 있는 지역변수인 campgrounds를 전역변수로 재설정하여 /campgrounds/new 에서 해당 array에 접근할 수 있도록 한다. 만약 campgrounds array가 지역변수로 되어 있을 경우 다른 메소드에서 해당 값에 접근이 불가능하다.

[그림 5] 단순히 app.get("/campgrounds", (req, res)...... 메소드 내부에 있는 데이터를 밖으로 빼주면 된다.

 

3-2. /campgrounds로 들어온 POST 요청에 들어있는 name과 image 저장 

app.post("/campgrounds/new", (req, res) => {  // /campgrounds/new라는 주소를 post로 요청받으면 아래 로직 수행
	const name = req.body.name		// 서버로 요청된 body태그 내의 name이란 name을 가진 데이터를 const name에 저장
    const image = req.body.image 	// 서버로 요청된 body태그 내의 image란 name을 가진 데이터를 const image에 저장
    const newCampground = {name: name, image: image} // 위의 전달 받은 데이터를 newCampground object에 저장 
    campgrounds.push(newCampground) // 전역변수 campground array에 newCampground object를 push(입력) 한다.
    res.redirect("/campgrounds")    // 마지막으로 응답 시 /campgrounds로 페이지를 리다이렉트 시킨다. 
});
    

최종적으로 저장된 값은 그림 5의 const campgrounds에 저장된다. 

 

 

3-3. campgrounds.ejs에 새로운 name과 image를 추가할 수 있는 페이지로 이동하는 링크 추가

<%- include("partials/header%>

<h1> This is the campgrounds page! </h1>
<a href="/campgrounds/new">Add New Campground</a>		<!--/campgrounds/new로 이동하는 링크 추가-->

<% campgrounds.forEach((campground) => {%>
  <div>
        <h4>
            <%= campground.name %>
            <img src="<%= campground.image %>">
        </h4>
  </div>
<%});%>

<%- include("partials/footer") %>

 

 

4. 실제 서버 동작여부 테스트

4-1. /campgrounds로 접속

[그림 6] Add new Campground 링크 클릭

 

4-2. /campgrounds/new 페이지에서 실제 데이터 입력 및 submit 버튼 클릭

[그림 7] 필드에 데이터 입력

 

 

[그림 8] Reqest Method: POST 로 [그림 7]에서 입력한 데이터를 클라이언트에서 서버로 전달.  

 

 

[그림 9] 실제로 express 서버에 해당 데이터가 전달됨. 

 

1. header.ejs // footer.ejs 생성

: header 및 footer 사용은 필수가 아님. 다만 DRY(Don't Repeate Yourself)를 적용하기 위함이다. 

 

[그림 1]YempCamp 기준으로 생성된 파일 목록

그림 1과 같이 views/partials 아래에 header.ejs와 footer.ejs를 생성한다. 

 

 

2. header.ejs 및 footer.ejs에 다음과 같이 html 코드를 입력한다. 

 

1) header.ejs

<DOCTYPE>
<html>
	<head>
		<title>Yelpcamp</title>
	</head>

2) footer.ejs

	<p>TradeMark YelpCamp 2020</p>
</body>

 위의 header.ejs와 footer.ejs를 합치면 기본적인 html 파일의 형태가 나온다. 즉 두 파일을 res.render로 서버에서 클라이언트로 response 시 전송할 스크립트 앞 뒤로 붙이도록 설정하는 것이다. 

 다시 말해 Yelpcamp의 모든 페이지는 위와 같은 header와 footer가 조립되어 클라이언트로 전달된다. 

 

 

3. res.render에서 사용하는 스크립트에 header와 footer 호출하는 코드 삽입

 

1) landing.ejs

<%- include("partials/header") %>		<!--2에서 작성한 header.ejs 호출-->

<h1>Landing Page</h1>
<p> Welcome to Yelpcamp </p>
<a href="/campgrounds"> Back to campgrounds page <a/>   <!-- campgrounds 페이지로 이동시키는 링크-->

<%- include("partials/footer") %>		<!--2에서 작성한 footer.ejs 호출-->

2) campground.ejs

<%- include("partials/header") %>				<!-- 2에서 작성한 header.ejs 호출-->
<% campgrounds.forEach((campground) => {		<!-- forEach loof로 app.js에 있는 const campground 반복호출 -->
	<div>
    	<%= campground.title %>					<!-- forEach loof 돌 때 title을 뽑아서 출력-->
        <img src="<%= campground.image %>">		<!-- forEach loof 돌 때 image을 뽑아서 출력-->
    </div>
<%})%>

<%- include("partials/footer") %>				<!-- 2에서 작성한 footer.ejs 호출-->

 

<img> 태그에 표현식을 위와 같이 했을 경우 실제로 server에서 리턴하는 코드는 아래와 같으며, 정상적으로 img를 출력하는 코드를 브라우저에서 동작시켜 이미지를 출력시킨다. 

<img src="https://img1.daumcdn.net/thumb/R800x0/?scode=mtistory2&fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F1340BB3D5030CCFE02">

 

header / footer / forEach 가 정상적으로 적용되어 서버에서 브라우저로 리턴된 페이지 모습

 

 

4. 마지막으로 페이지를 디자인 하기 위해 BootStrapCDN을 header.ejs에 추가해준다.

<!DOCTYPE html>
<html>
	<head>
		<title>Yelpcamp</title>
		<link ref="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
   </head>
   <body>

 

해당 에러 발생 원인은 크게 2가지 입니다.

 

1. pip install pygame 즉, pygame 모듈이 설치되지 않았을 경우

 

2. 내가 생성 혹은 지금 실행중인 파일 이름이 pygame으로 저장되어 있을 경우

-> 해당 경우에 Python에서 실제 pygame module에 있는 locals를 import 하는 것이 아니라 pygame.py 라는 파일에서 해당 내용을 import 하려고 하기 때문에 에러가 발생합니다.

-> 즉, 현재 생성한 파일 이름이 pygame.py 일 경우 다른 이름으로 수정 후 실행시키시면 정상적으로 import 가능하실 겁니다. 

+ Recent posts