3주차까지는 Fetch를 사용해서 서버에 올라와있었던 JSON 데이터로부터 내가 필요한 정보만을 가져왔었다. 그리고 서버와 클라이언트 개념을 이해했었는데,
이제까지 내가 만든 사이트들을 보면, 내가 직접 html 코드를 작성하고 [기록하기] 버튼을 눌러서 페이지에 새로 앨범 카드가 쓰여진 걸 확인할 수 있었지만, 새로고침하면 모든 것이 무색하게 초기 상태로 돌아갔다.
다시 한번, 왜 이렇게 된 것일까?
아무리 클라이언트 페이지에서 HTML 데이터를 만들어서 페이지 구조를 덧붙인다고 한들, 결국 서버-클라이언트 구조에서는 페이지를 새로 로드할 시 서버에서 모든 페이지 정보를 가져와서 사용자에게 표시하기만 할 뿐이다. 즉 내가 서버에 내 데이터를 옮겨서 저장해 놓지 않는다면, 내가 만들었던 html 코드는 아무런 의미가 없어지는 것이다!
결국 서버에 데이터를 저장할 방법이 필요한데, 여기에서 데이터베이스 개념이 등장한다.
먼저 데이터베이스란 무엇이며, 왜 필요할까?
먼저 정의를 살펴보면, 데이터를 저장하고 여러 사람들이 관리하는 데이터의 모음을 뜻한다.
그렇다면 왜 필요할까?
이것에 앞서서 우리가 책을 책꽂이에 보관하는 이유에 대해 살펴보자.
책을 그냥 바닥에 stack up 해서 방에 두어도 되는데, 왜 굳이 책꽂이에 한권씩 꽂아서 보관할까?
그것은 바로, 잘 보관하기 위해서, 또 잘 찾기 위해서이다.
데이터베이스에 데이터를 보관하는 것 역시 데이터를 더 잘 보관하고, 더 잘 찾기 위해서이다.
데이터를 그냥 마구잡이로 보관하면 데이터를 꺼내 쓸 때마다 방에 아무런 order 없이 보관되어 있는 데이터들을 모두 순회해서 내가 찾는 데이터 값과 일치하는지 확인해야 한다. 데이터가 한두개라면 상관이 없겠지만, 백만개, 몇십억개, 점점 늘어날수록 시간은 늘어날 것이다.
하지만 내가 데이터베이스에 데이터를 정리했다면, ORDER_ID, CUSTOMER_ID와 같이 정렬된 COLUMN에서의 각 데이터 값을 순차적으로 보관할 수 있고, 데이터 수가 아무리 많아도 즉시 내가 필요한 데이터 값을 추출할 수 있다.
그렇다면 이제는 데이터베이스의 종류에 대해 알아보자.
먼저 크게 데이터베이스는 SQL과 NoSQL로 나뉜다.
NoSQL의 No는 우리가 아는 No는 아니고, Not Only의 줄임말이다.
정의를 보면 SQL은 관계형 데이터베이스, 그리고 NoSQL은 비관계형 데이터베이스인데,
크게 어렵지 않게 이해하려면 정리된 정보를 다룰 때 유리한 데이터베이스가 SQL이고, 복잡하거나 유연한 정보를 다룰 때 사용하는 데이터베이스가 NoSQL이라는 것이다.
예를 들어 스타트업 회사를 꾸려 나가는 중이라면, 비즈니스 모델이 아직 Fixed되지 않은 상황에서 크고 작은 데이터 구조가 많이 바뀔 수 있다. 이런 상황에서는 NoSQL이 유리하다. 그렇지 않은 대기업이나 은행에서는 SQL이 더 좋다.
오늘 데이터의 저장을 위해 사용해 볼 외부 프로그램은 바로 Firebase, Firestore라고 하는 데이터베이스다. 이것은 NoSQL 을 기반으로 한다.
=============================================
먼저 Firebase란, 구글이 개발한 모바일 및 웹 애플리케이션 개발 플랫폼이다.
즉 웹 서버를 대신 만들어 주는 서비스라고 이해하면 쉽다.
서버 개발이 없어도 웹을 만들 수 있어서, 사용하기 좋은 플랫폼이라고 할 수 있다!
이걸 사용하는 방법은..
먼저 구글에 firebase를 검색한 후 로그인을 한 후, Go to Console 버튼을 누른다.
그리고 프로젝트 만들기를 누른 후, 프로젝트 이름을 지정하고 계속을 누른다.
프로젝트 준비가 완료되면, 웹을 선택해서 진행한다.
그리고 앱 닉네임을 적은 후 앱 등록을 누르면 된다.
마지막으로 <script> 태그 사용을 누르고 아래 내용을 복사해서 보관한다.
콘솔로 이동을 누르면, FireStore Database로 이동하게 된다.
FireStore는 데이터베이스 서비스로 데이터를 저장하고 관리할 수 있는 기능을 제공하는 구글의 클라우드 기반 NoSQL 데이터베이스다!
파이어스토어는 마치 큰 창고와 같아서, Collection, Document, Field로 각각 데이터를 구분해서 보관한다.
* Collection: 서랍장 그룹과도 같다. 여러 개의 문서들이 특정한 주제 또는 유형으로 그룹화되어 있다.
* Document: 서랍장 안에 들어 있는 작은 종이와도 같다. 하나의 종이는 여러 개의 필드로 구성되어 있고, 각 필드는 종이에 저장된 데이터를 의미한다.
* Field: 문서 안에 있는 데이터의 작은 부분과도 같다. 각 필드는 값으로 구성된다.
이제 파이어스토어를 시작해보자!
===============================================
파이어스토어 데이터베이스를 선택하고,
데이터베이스 만들기를 누른다.
Cloud Firestore 위치를 서울로 설정하고, 프로덕션 모드에서 시작을 누르고 사용을 누르면..
이 화면이 뜨는데, 여기에서 규칙을 누르고,
보이는 텍스트 내에서 false는 true로 바꾼 후에 게시를 누른다!
마지막으로 프로젝트 개요 옆 톱니바퀴를 눌러서 프로젝트 설정에 들어가면, 내 앱 섹션에서 SDK 설정 및 구성 => 구성을 누른다.
그리고 아래 나오는 코드를 복사해서 보관한다.
그리고 아래 코드는 FireStore 세팅 코드니, 복사해서 붙여넣기를 해주면 된다.
// Firebase SDK 라이브러리 가져오기
// Firebase 구성 정보 설정
const firebaseConfig = {
본인 설정 내용 채우기
};
// Firebase 인스턴스 초기화
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
이 코드를 같이 사용할 거다. 이제 웹사이트를 만들어 보자!
================================
우선 지금까지 완성된 앨범 코드는 이렇게 생겼다.
<! doctype html >
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, shrink-to-fit=no" >
< title > 나만의 추억앨범 </ title >
integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous" >
< style >
* {
font-family : 'Gowun Dodum' , sans-serif ;
}
.mytitle {
background-color : green ;
color : white ;
height : 250px ;
/* 내용물을 정렬 */
display : flex ;
flex-direction : column ;
align-items : center ;
justify-content : center ;
background-position : center ;
background-size : cover ;
}
.mytitle > button {
width : 150px ;
height : 50px ;
background-color : transparent ;
border : none ;
color : white ;
font-size : 18px ;
font-weight : bold ;
border-radius : 5px ;
border : 1px solid white ;
margin-top : 20px ;
}
.mycards {
margin : 20px auto 20px auto ;
width : 1200px ;
}
.mypostingbox {
width : 500px ;
margin : 20px auto 20px auto ;
padding : 20px 20px 20px 20px ;
border-radius : 5px ;
box-shadow : 0px 0px 3px 0px blue ;
}
.mybtn {
display : flex ;
flex-direction : row ;
align-items : center ;
justify-content : center ;
margin-top : 10px ;
}
.mybtn > button {
margin-right : 10px ;
}
</ style >
< script >
function openclose () {
$ ( '#postingbox' ). toggle ();
}
function makeCard () {
let image = $ ( '#image' ). val ();
let title = $ ( '#title' ). val ();
let content = $ ( '#content' ). val ();
let date = $ ( '#date' ). val ();
let temp_html = `
<div class="col">
<div class="card h-100">
<img src=" ${ image } "
class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"> ${ title } </h5>
<p class="card-text"> ${ content } </p>
</div>
<div class="card-footer">
<small class="text-muted"> ${ date } </small>
</div>
</div>
</div>` ;
$ ( '#card' ). append ( temp_html );
}
</ script >
</ head >
< body >
< div class = "mytitle" >
< h1 > 나만의 추억 앨범 </ h1 >
< button onclick = " openclose ()" > 추억 저장하기 </ button >
</ div >
< div class = "mypostingbox" id = "postingbox" >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "image" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 이미지 </ label >
</ div >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "title" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 제목 </ label >
</ div >
< div class = "form-floating" >
< input type = "email" class = "form-control" id = "content" placeholder = "name@example.com" >
< label for = "floatingTextarea" > 앨범 내용 </ label >
</ div >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "date" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 날짜 </ label >
</ div >
< div class = "mybtn" >
< button onclick = " makeCard ()" type = "button" class = "btn btn-dark" > 기록하기 </ button >
< button type = "button" class = "btn btn-outline-dark" > 닫기 </ button >
</ div >
</ div >
< div class = "mycards" >
< div id = "card" class = "row row-cols-1 row-cols-md-4 g-4" >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
</ div >
</ div >
</ body >
</ html >
데이터베이스에 데이터를 저장하는 과정을 하려면, 오늘 나는 Firebase + Firestore를 사용하기 때문에 세팅을 미리 해줘야 한다.
먼저 <script> 태그에 타입명을 적어준다. 이름은 'module'.
그리고 그 아래에 위에 붙여넣기용으로 추가한 텍스트를 복사한다.
그리고 '본인 설정 내용 채우기' 부분에 아까 <구성> 항목에서 복사해온 텍스트를 집어넣는다.
이렇게 하면 세팅이 끝!
이제 우리가 어느 순간 데이터베이스에 데이터를 저장할건지를 생각해 보면 된다.
바로 [기록하기] 버튼을 눌렀을 때인데, 데이터를 추가하기 위해 아래 코드를 사용할거다.
$ ( "#id" ). click ( async function () {
let doc = {};
await addDoc ( collection ( db , "콜렉션이름" ), doc );
})
[기록하기] 코드는 <body> 안에 있다.
[기록하기] 버튼을 누르면, 데이터베이스에 데이터를 저장한다는 의미로 addDoc() 함수를 사용한다. #id에는 이 [기록하기] 버튼의 id를 넣어주고, "콜렉션이름"에는 albums을 넣어준다.
$ ( "#postingbtn" ). click ( async function () {
let doc = {};
await addDoc ( collection ( db , "albums" ), doc );
})
그리고 makeCard()에 있던 변수들을 이곳으로 가져온다.
let image = $ ( '#image' ). val ();
let title = $ ( '#title' ). val ();
let content = $ ( '#content' ). val ();
let date = $ ( '#date' ). val ();
그리고 doc이라는 변수로 한곳에 모은다.
$ ( "#postingbtn" ). click ( async function () {
let image = $ ( '#image' ). val ();
let title = $ ( '#title' ). val ();
let content = $ ( '#content' ). val ();
let date = $ ( '#date' ). val ();
let doc = {
'image' : image ,
'title' : title ,
'content' : content ,
'date' : date
};
await addDoc ( collection ( db , "albums" ), doc );
})
또 [기록하기]를 누른 후에는 사용자에게 '기록이 완료되었습니다'라는 메시지를 출력하고,
postingbox를 새로고침해서 다시 초기 상태로 되돌린다.
새로고침을 위해서는 window.locaion.reload()를 사용한다.
여기까지 완료되었다면, 아래와 같은 코드가 완성된다.
더보기
<! doctype html >
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, shrink-to-fit=no" >
< title > 나만의 추억앨범 </ title >
integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous" >
< style >
* {
font-family : 'Gowun Dodum' , sans-serif ;
}
.mytitle {
background-color : green ;
color : white ;
height : 250px ;
/* 내용물을 정렬 */
display : flex ;
flex-direction : column ;
align-items : center ;
justify-content : center ;
background-position : center ;
background-size : cover ;
}
.mytitle > button {
width : 150px ;
height : 50px ;
background-color : transparent ;
border : none ;
color : white ;
font-size : 18px ;
font-weight : bold ;
border-radius : 5px ;
border : 1px solid white ;
margin-top : 20px ;
}
.mycards {
margin : 20px auto 20px auto ;
width : 1200px ;
}
.mypostingbox {
width : 500px ;
margin : 20px auto 20px auto ;
padding : 20px 20px 20px 20px ;
border-radius : 5px ;
box-shadow : 0px 0px 3px 0px blue ;
}
.mybtn {
display : flex ;
flex-direction : row ;
align-items : center ;
justify-content : center ;
margin-top : 10px ;
}
.mybtn > button {
margin-right : 10px ;
}
</ style >
< script type = "module" >
// Firebase SDK 라이브러리 가져오기
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey : "AIzaSyAftiw-pWmG1C4Q-fUXowlhOyEtnpR6N2U" ,
authDomain : "sparta-e5f34.firebaseapp.com" ,
projectId : "sparta-e5f34" ,
storageBucket : "sparta-e5f34.appspot.com" ,
messagingSenderId : "401566102781" ,
appId : "1:401566102781:web:5f45ff16a418853068ba94" ,
measurementId : "G-ZQS5NQ3FBQ"
};
// Firebase 인스턴스 초기화
const app = initializeApp ( firebaseConfig );
const db = getFirestore ( app );
$ ( "#postingbtn" ). click ( async function () {
let image = $ ( '#image' ). val ();
let title = $ ( '#title' ). val ();
let content = $ ( '#content' ). val ();
let date = $ ( '#date' ). val ();
let doc = {
'image' : image ,
'title' : title ,
'content' : content ,
'date' : date
};
await addDoc ( collection ( db , "albums" ), doc );
alert ( '저장이 완료되었습니다.' );
window . location . reload ();
})
$ ( "#savebtn" ). click ( async function () {
$ ( '#postingbox' ). toggle ();
})
$ ( document ). ready ( function () {
fetch ( url ). then ( res => res . json ()). then ( data => {
let mise = data [ 'RealtimeCityAir' ][ 'row' ][ 0 ][ 'IDEX_NM' ]
$ ( '#msg' ). text ( mise )
})
})
function makeCard () {
let image = $ ( '#image' ). val ();
let title = $ ( '#title' ). val ();
let content = $ ( '#content' ). val ();
let date = $ ( '#date' ). val ();
let temp_html = `
<div class="col">
<div class="card h-100">
<img src=" ${ image } "
class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"> ${ title } </h5>
<p class="card-text"> ${ content } </p>
</div>
<div class="card-footer">
<small class="text-muted"> ${ date } </small>
</div>
</div>
</div>` ;
$ ( '#card' ). append ( temp_html );
}
</ script >
</ head >
< body >
< div class = "mytitle" >
< h1 > 나만의 추억 앨범 </ h1 >
< p > 현재 서울의 미세먼지 : < span id = "msg" > 나쁨 </ span ></ p >
< button id = "savebtn" > 추억 저장하기 </ button >
</ div >
< div class = "mypostingbox" id = "postingbox" >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "image" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 이미지 </ label >
</ div >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "title" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 제목 </ label >
</ div >
< div class = "form-floating" >
< input type = "email" class = "form-control" id = "content" placeholder = "name@example.com" >
< label for = "floatingTextarea" > 앨범 내용 </ label >
</ div >
< div class = "form-floating mb-3" >
< input type = "email" class = "form-control" id = "date" placeholder = "name@example.com" >
< label for = "floatingInput" > 앨범 날짜 </ label >
</ div >
< div class = "mybtn" >
< button id = "postingbtn" type = "button" class = "btn btn-dark" > 기록하기 </ button >
< button type = "button" class = "btn btn-outline-dark" > 닫기 </ button >
</ div >
</ div >
< div class = "mycards" >
< div id = "card" class = "row row-cols-1 row-cols-md-4 g-4" >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
< div class = "col" >
< div class = "card h-100" >
class = "card-img-top" alt = "..." >
< div class = "card-body" >
< h5 class = "card-title" > 앨범 제목 </ h5 >
< p class = "card-text" > 앨범 내용 </ p >
</ div >
< div class = "card-footer" >
< small class = "text-muted" > 앨범 날짜 </ small >
</ div >
</ div >
</ div >
</ div >
</ div >
</ body >
</ html >
먼저 기록하기 버튼을 누르기 전 모습이다.
데이터베이스가 비어 있는 것을 볼 수 있다.
기록하기 버튼을 누르면,
메시지와 함께 데이터베이스에 데이터가 들어가 있는 모습을 볼 수 있다.
성공적으로 데이터를 데이터베이스에 저장했다!
=====================================
이제 데이터베이스에서 데이터를 꺼내 올 시간이다.
let docs = await getDocs ( collection ( db , "콜렉션이름" ));
docs . forEach (( doc ) => {
let row = doc . data ();
console . log ( row );
});
addDocㄴ와 비슷한 이름으로 만든 getDocs이다. 마찬가지로 콜렉션 이름에 albums를, 그리고 row에 저장되어 있는 데이터를 html 형식으로 변환하면 된다.
let row = doc . data ();
let image = row [ 'image' ]
let title = row [ 'title' ]
let content = row [ 'content' ]
let date = row [ 'date' ]
그리고 카드 생성을 위해서 temp_html을 다시 만들고, 웹사이트가 열리자마자 카드가 자동으로 생성되도록 하면 된다!
let docs = await getDocs ( collection ( db , "albums" ));
docs . forEach (( doc ) => {
let row = doc . data ();
let image = row [ 'image' ];
let title = row [ 'title' ];
let content = row [ 'content' ];
let date = row [ 'date' ];
let temp_html = `
<div class="col">
<div class="card h-100">
<img src=" ${ image } "
class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"> ${ title } </h5>
<p class="card-text"> ${ content } </p>
</div>
<div class="card-footer">
<small class="text-muted"> ${ date } </small>
</div>
</div>
</div>` ;
$ ( '#card' ). append ( temp_html );
});
그리고 저장 후 웹사이트를 열어 보면
데이터베이스에 저장되었던 정보가 잘 붙여진 모습을 볼 수 있다.
오늘은 여기까지! 마지막 5주차 시간에는 Github에 대해 알아보고, Github Pages를 이용해서 배포하는 작업을 해보자.