개괄

항해에 들어간 후 첫 프로젝트를 진행하였다.  

 

목표

프로젝트는 flask와 html, css를 활용한 youtube player를 구현하기로 했고,

session을 이용한 로그인 화면 구현과 bs4를 이용한 유튜브 search result를 크롤링해서 플레이리스트에 추가, 삭제하기

등이 주요 과제가 되었다.

 

배운 것 - TIL SERIES (Link)

REST-API : 누구나 읽기 쉽게 설계하기

https://choincnp.tistory.com/21

 

TIL - REST API : 누구나 읽기 쉽게 설계하기

REST란? REpresentational State Transfer의 약자로, 자원(리소스)의 표현에 의한 상태 전달을 하는, HTTP를 잘 활용하기 위해 만든 ‘아키텍쳐’이다. 따라서 REST를 잘 지키지 않는다고 해서 ‘trash code’나

choincnp.tistory.com

협업 - 합리적인 소통하기

https://choincnp.tistory.com/18

 

웹 개발 프로젝트 목표 설정하기

첫 팀이 분배되고, 프론트 지향 2명과 백엔드 지향 2명이 같은 팀이 되었다. 문제점 프로젝트 주제에 대해서는 많은 아이디어가 나왔지만, 그 주제에 어떤 기능을 넣어야 할지, 모여서 무엇을 해

choincnp.tistory.com

git flow - trunk-based 기법을 활용한 팀원들의 소통

db 저장하기 - Mongo DB를 활용한 dictionary를 element로 가진 Array를 저장하기

0주차 Troubleshooting

 

 

목표 달성 여부

결과적으로 말하자면, 절반의 성공과 절반의 실패라고 볼 수 있다.

GIT REPOSITORY : https://github.com/choincnp/duoback_mini

<main page>

좌측 상단 : iframe을 이용한 현재 재생중인 유튜브 영상

좌측 하단 : mongoDB를 이용한 내가 선택한 playlist

우측 상단 : search tab을 활용한 유튜브 영상 검색

우측 하단 : search result

<login>

pymongo를 이용해 로그인 페이지와 회원가입 페이지를 만들고, ID와 PW는 몽고DB에서 관리, 로그인 정보는 session에서 관리하게 만들었다.

클라이언트단은 완벽하지만, 서버단의 구조는 매우 빈약한것을 볼 수 있다. 조금만 더 알았더라면, 조금만 더 공부했더라면 다른 팀원들이 구현하고자 했던 기능들을 더 구현할 수 있었는데, 내 능력의 부족으로 이루지 못해 너무 속상했고, 각오를 다졌다.

 

 

느낀 점

컴퓨터는 앞으로만 가지만, 사람은 양방향성을 띈다.

앞으로만 가는 컴퓨터의 활동을 제어하는것은 쉽지만, 때로는 전진하고, 때로는 후퇴하는 사람을 제어하는것은 훨씬 힘든 일이다. 첫 협업에서부터 삐끗했던 활동을 생각하며, 옛날에 떠올렸던 '이런 사람과도 협업을 못하는데, 내가 과연 smooth한 협업을 진행할 수 있을까?'라는 생각을 다시금 했다.

 

내 코드는 컴퓨터만 읽는 게 아니라, 사람도 읽는다.

제발한번만

컴퓨터가 읽기 좋은 코드로 개발하는것도 좋지만, 사람이 읽기 좋은 코드로 개발하는것도 꼭 필요한 프로세스다.

읽기 쉽게 코딩하지 않으면 남들이 보는 데 거부감이 든다.

 

제대로 알지 못하면, 안 하느니만 못하다.

인강을 다 들었어도 실제 구현을 하려고 하면 막막하기도 하고, 도대체 내가 무엇을 하고 있는지, 어디에 있는지조차 헷갈린다. 

 

 

다음 주 목표

- 위의 것들을 기반으로 한 원활한 소통과 협업이 이루어지는, '케미스트리'가 가득한 오케스트라 같은 팀 구성하기.

- RESTful한 API 설계로 모르는 사람이 보아도 한 눈에 무슨 api인지 알게 하기

- code-convention과 commit-convention으로 명확한 의미를 지닌 코드/소통 설계하기

 

 

웹 서비스를 런칭하기 위해선...

  • 언제나 요청에 응답할 수 있어야 한다. (컴퓨터가 항상 실행 중이어야 한다)
  • '모두가 접근할 수 있는' 공개 IP주소로 내 서비스에 접근할 수 있어야 한다.

서버는 그냥 '컴퓨터'이기 때문에 우리는 가상 컴퓨터인 Amazon EC2(인스턴스)를 이용해 구동하기로 한다.

 

주의사항

아마존은 자선사업가가 아니기 때문에  컴퓨터를 무제한 제공해주지는 않는다.

free tier로 가입을 하더라도 컴퓨터를 계속 켜놓으면 돈이 나간다.

이런 메세지를 무시하다간 피같은 나의 돈이 나간다.

예전에 서버 구동을 따라해보고 깜빡 잊었다가 돈이 나갔었던 기억에 이같은 경고 메세지를 남긴다.

가끔 선량한 대학생이 1학년때 팀프로젝트로 EC2를 돌렸다가 까먹고 대학교 4학년때 billing으로 몇백만원이 청구되었다는 썰도 심심찮게 들리니, 

꼭 인스턴스를 '중지' 해주도록 하자!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

참고로 인스턴스 종료를 누르면 컴퓨터를 반납하는거라서 여러분들의 소중한 작업물이 다 날라간다.

 

EC2 연결하기

Git bash에서 SSH(Secure Shell Protocol)이라는 방식으로 접속을 할 건데, SSH방식은 그냥 조금 더 보안에 안전한 프로토콜이라고 생각하면 된다. pem키를 받아서 써야 한다.

모자이크된 부분에 본인의 키 위치를 넣고, ubuntu@뒤에는 인스턴스의 공용 IP Address를 누르면 된다.

만약 인스턴스를 중지했다가 다시 켤 경우, 공용 IP주소가 변하기 때문에 바꿔줘야 한다.

다음은 flask 환경에서 설정하는 3단계이다.

#1. python3 -> python
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10

#2. pip3 -> pip
sudo apt-get update
sudo apt-get install -y python3-pip
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

#3. port forwarding
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000

 

  1. 첫 번째 명령어는 python3 ~~~~ 라고 해야하는 명령어를 python ~~~~로 바꿔주는 명령어다.
    프로그래머는 귀찮아해야한다. 그래야 더 나은 방식을 생각할 수 있으니까.
  2. 두 번째 명령어는 flask 환경에서 구동할 때 쓰이는 패키지들을 관리해주는 패키지 툴인 pip3를 설치하고,
    이름을 pip로 바꿔주는 것이다.
  3. 이게 가장 중요한데 우리가 계속 진행해왔던 localhost:5000에서 이 포트번호 5000을 포트번호 80으로
    리다이렉팅 해준다는 것이다. 리다이렉팅과 포워딩의 차이는 추후에 다시 설명하겠다.

그리고 Filezila에서 다시 연동하고 아마존의 인스턴스에 실행할 app.py나 index 등을 넣어준 후,
ls(list : 데이터 목록 보여주기)

cd(change directory : 디렉토리 변경)

mkdir(make directory : 디렉토리 생성)

pip install packagename(파이썬의 packang

등의 명령어를 입력해 '알아서 잘' 하고 python app.py 명령어로 가동시키면 된다.

 

인바운드 규칙 추가

인바운드 규칙 추가는 우리가 사용할 인스턴스의 포트번호를 추가해준다.

이렇게 TCP/5000 포트와 HTTP/80포트를 열어주어야 비로소 우리가 만들었던 홈페이지에 접속 가능하다.

 

80포트의 비밀

우리가 접속하는 대부분의 인터넷 사이트(네이버, 다음 등)는 사실 #http://www.naver.com:80 인데, 80포트가 생략된 것이다. 80은 생략이 가능하기 때문에. 그래서 아까 우리가 포트포워딩으로 5000번을 80에 연결해놓은 것이다. 그러면 접속할때마다 사이트 뒤에 5000포트를 생략해도 된다.

이 포트포워딩은 리눅스에서 자동 지원된다.

 

nohup 설정하기

nohup은 우리가 인스턴스에서 구동한 app.py를, git bash를 종료해도 구동될 수 있도록 하는것이다. 방법은 매우 쉽다.

그냥 git bash에 nohup python app.py를 써주면 계속 돌아간다.

나는 왜 그런진 모르겠지만 습관적으로 nohub를 써주는 경향이 있어 조금 더 주의를 요해야 한다. 왜 그럴까?

키는 건 알겠는데...

끌때는 다음과 같은 코드를 넣어주면 된다.

ps -ef | grep 'python app.py' | awk '{print $2}' | xargs kill

 

OG(Open Graph) tag

미리보기가 없다면 여기서 뭘 하라는건지, 무슨 사이트인지 모를 수도 있다.

OG태그는 위의 사진과 마찬가지로 미리보기 image, title, description등을 삽입하는 것이다.

그냥 <head></head>안에 다음과 같은 3줄을 심어주면 된다.

<meta property="og:title" content="내 사이트의 제목" />
<meta property="og:description" content="보고 있는 페이지의 내용 요약" />
<meta property="og:image" content="이미지URL" />

이로써 길다면 길고 짧다면 짧았던 5주차(라고 쓰고 5시간짜리) 수업이 끝났다.

 

웹 개괄을 이보다 더 잘 나타내준 수업은 없었다. 정말 감사한 수업이었다.

두 번째, 스파르타피디아

저번에 만들었던 스파르타피디아에 추가로 크롤링 기능까지 더해본다

조각 기능?

프로젝트에 들어갈 기능들을 미리 구현해보는것.

이번 프로젝트에서 meta tag를 크롤링하는 기능을 새로 구현해야하니 먼저 이 기능이 잘 되는지부터 검증해야 한다.

나중에 찐프로젝트를 들어가면 수십~수백가지 기능을 구현해야 하는 데,

모든 기능을 한꺼번에 돌리는것은 시간도 많이 소요되고 어떤 기능에 문제가 있어서 오류가 나는지 쉽사리 찾기 어렵다.

따라서 이런 작은 기능 하나하나에 대한 검증이 필요하다.

이런 조각 기능들을 검증하면서 개발을 진행하는것이 TDD(Test-Driven Development)의 기초가 되니, 열심히 해 두자.

 

META TAG 스크래핑 하기

과제를 끝낸 후의 내사랑 다펑의 OG태그

카톡에 URL을 전송하면, 썸네일과 title, 그리고 description이 자동으로 붙는다.

이것들은 어떻게 남기고, 어떻게 가져올까?

 

Meta tag의 위치

친절하게도, head를 까보면 다 들어있다. 여기의 프로퍼티를 가져오자.

우리가 찾고있던 것들은, og:property라는 이름 안에 있는 content에 들어있다.

이번에는 저번과 다른 방식으로 크롤링을 사용해 볼 것이다.

import requests
from bs4 import BeautifulSoup

url = 'url주소'

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url,headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')
#여기까진 똑같다

og_image = soup.select_one('meta[property="og:image"]')
og_title = soup.select_one('meta[property="og:title"]')
og_description = soup.select_one('meta[property="og:description"]')
#의미하는 바 : soup에서 하나를 고를 건데, meta tag의 property가 "og:~~~~"인 애를 가져와라.

image = og_image['content']
title = og_title['content']
description = og_description['content']
#그리고 각각의 property 안에 있는 content를 가져올 것이다.

이렇게 하면 우리가 원하던 영화의 메타값의 image, title, description을 가져올 수 있다 :)

 

STEP 1 : 무엇을 고려해야 하는가?

  • URL은 어떻게 만들 것인지, 요청 방식은 어떻게 될 것인지?
  • 클라이언트에서 서버로는 어떤 것을 넘겨 줄 것인지?
  • 서버에서 클라이언트로는 어떤 것을 넘겨 줄 것인지?

STEP 2 : 서버단 설정 (POST방식)

@app.route("/movie", methods=["POST"])
def movie_post():
    url_receive = request.form['url_give']
    star_receive = request.form['star_give']
    comment_receive = request.form['comment_give']
#클라이언트로 ~~~~_give들을 각각 받아와서 서버단의 변수에 담아준다.
#이제는 받은 url을 가지고 크롤링을 해 줄 것이다.
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
    data = requests.get(url_receive, headers=headers)
#받아온 데이터의 url을 data에 저장하고
    soup = BeautifulSoup(data.text, 'html.parser')
#그 데이터를 soup을 이용해 파싱해준다.
    image = soup.select_one('meta[property="og:image"]')['content']
    title = soup.select_one('meta[property="og:title"]')['content']
    description = soup.select_one('meta[property="og:description"]')['content']
#그 파싱한 데이터에서 메타데이터를 추출한다.

    doc = {
        'image':image,
        'title':title,
        'desc':description,
        'star':star_receive,
        'comment':comment_receive
    }
#그리고 함께 넘어온 request중 star와 comment를 모두 DB에 담고 JSON형태로 넘겨준다.

    db.movies.insert_one(doc)

    return jsonify({'msg':'POST 연결 완료!'})

STEP 3 : 클라이언트단 설정

function posting() {
    let url = $('#url').val()
    let star = $('#star').val()
    let comment = $('#comment').val()
# class name이 url, star, comment인것의 아규먼트를 각각 담는다.
    $.ajax({
        type: 'POST',
        url: '/movie',
#우리는 POST방식으로 movie라는 창구에서 데이터를 다룰거라고 말 하는 중
        data: {url_give: url, star_give: star, comment_give: comment},
#JSON방식으로 데이터를 다룰 것인데, data는 {param : value}형태로 들어가야 하므로
#~~~~_give에 ~~~~를 담아서 클라이언트에 넘겨준다.
        success: function (response) {
            alert(response['msg'])
            window.location.reload()
#문제가 없을 경우 서버에서 받은 메세지를 띄우고 새로고침한다.
        }
    });
}

 

GET방식도 그대로 하면 된다.

STEP 2 : 서버단 설정(GET방식)

*POST가 아니므로 클라이언트에서 서버로 가져갈 것이 없기 때문에 간단하게 쓸 수 있다.

@app.route("/movie", methods=["GET"])
def movie_get():
    movie_list = list(db.movies.find({},{'_id':False}))
    return jsonify({'movies':movie_list})

STEP 3 : 클라이언트단 설정

$(document).ready(function(){
	  listing();
#화면이 다 로딩되면 listing 함수를 실행한다.
});

function listing() {
    $('#cards-box').empty()
    $.ajax({
        type: 'GET',
        url: '/movie',
        data: {},
        success: function (response) {
#아까 movie_list를 movies라는 변수에 담아 response에 넘겨주었었다.
            let rows = response['movies']
            for(let i = 0; i < rows.length; i++) {
                let image = rows[i]['image']
                let title = rows[i]['title']
                let desc = rows[i]['desc']
                let star = rows[i]['star']
                let comment = rows[i]['comment']

                let star_image = '⭐'.repeat(star)
#repeat 함수는 파이썬이 얼마나 좋은 언어인지를 알려준다.
                let temp_html = `<div class="col">
                                    <div class="card h-100">
                                        <img src="${image}"
                                             class="card-img-top">
                                        <div class="card-body">
                                            <h5 class="card-title">${title}</h5>
                                            <p class="card-text">${desc}</p>
                                            <p>${star_image}</p>
                                            <p class="mycomment">${comment}</p>
                                        </div>
                                    </div>
                                </div>`

                $('#cards-box').append(temp_html)
            }
        }
    })
}

첫 번째, 화성 땅 공동구매

이름, 주소, 평수를 고르고 주문버튼을 누르면, 주문한 내역들이 밑에 뜨게 된다.

STEP 1 : 무엇을 고려해야 하는가?

  • URL은 어떻게 만들 것인지, 요청 방식은 어떻게 될 것인지?
  • 클라이언트에서 서버로는 어떤 것을 넘겨 줄 것인지?
  • 서버에서 클라이언트로는 어떤 것을 넘겨 줄 것인지?

STEP 2 : 서버단 설정 (POST방식)

@app.route("/mars", methods=["POST"])
def mars_post():
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']
 #클라이언트로부터 name, address, size를 받아와서 각각 @_receive 변수에 담아놓고,
    doc = {
        'name': name_receive,
        'address': address_receive,
        'size': size_receive
    }
#우리는 NoSQL인 MongoDB의 방식을 따르니 json형태로 저장을 해 두고
    db.mars.insert_one(doc)
#DB에 인서트하고
    return jsonify({'msg': '주문 완료!'})
#response로 json형식의 '주문 완료'메세지를 던져준다.

STEP 3 : 클라이언트단 설정

function save_order() {
    let name = $('#name').val()
    let address = $('#address').val()
    let size = $('#size').val()
#name, address, size에 무엇이 들어갈 지 체크해둔다.
    $.ajax({
        type: 'POST',
        url: '/mars',
#Post 방식으로 /mars라는 창구에서 일을 볼 것이고
        data: { name_give:name, address_give:address, size_give:size },       
#data는 {param : value}의 형태로 저장할 건데
#{requestparam인 ~~~~_give : responsevalue인 ~~~~}의 형태로 받아올 것이다
        success: function (response) {
#문제가 없었으면 response값을 가지고 function을 실행하는데
            alert(response['msg'])
#response의 msg값을 alert로 알려줄 것이다.
            window.location.reload()
#그리고 reload()를 통해 새로고침을 한번 해 줄것이다.
        }
    });
}

 

GET방식도 그대로 하면 된다.

STEP 2 : 서버단 설정(GET방식)

*POST가 아니므로 클라이언트에서 서버로 가져갈 것이 없기 때문에 간단하게 쓸 수 있다.

@app.route("/mars", methods=["GET"])
def mars_get():
    orders_list = list(db.orders.find({},{'_id':False}))
#db에 있는 주문 정보를 전부 끌고와서 orders_list라는 변수에 담는다.
    return jsonify({'orders':orders_list})
#그리고 json형식으로 'orders'라는 파라미터에 전체 주문 정보를 담아준다.

STEP 3 : 클라이언트단 설정

 		$(document).ready(function () {
            show_order();
#모든 준비가 완료되면 show_order function을 실행시킨다.
        });
        function show_order() {
            $('#order-box').empty()
#원래 있던 order-box class를 전부 지운다.
            $.ajax({
                type: 'GET',
                url: '/mars',
                data: {},
#넘겨줄게 없으니 data는 공란으로 넘긴다.
                success: function (response) {
#서버단에서 넘겨준 response (전체 주문 정보)를 가지고 함수를 돌린다.
                    let rows = response['orders']
#rows는 response의 각각의 order 하나씩을 맡을 것이다.
                    for (let i = 0; i < rows.length; i++) {
                        let name = rows[i]['name']
                        let address = rows[i]['address']
                        let size = rows[i]['size']
#그리고 response의 name을 클라이언트에서 name으로,
#response의 address를 클라이언트의 address로
#response의 size를 클라이언트의 size라는 변수에 담아준다.
                        let temp_html = `<tr>
                                            <td>${name}</td>
                                            <td>${address}</td>
                                            <td>${size}</td>
                                          </tr>`
#각 name, address, size는 변동될 수 있으므로 EL문의 ${}에 담아주고
                        $('#order-box').append(temp_html)
#주문 정보를 나타내는 body class에 넣어주도록 한다.
                    }

                }
            });
        }

 

개괄

app.py파일을 통해 flask서버를 구동해서 api환경들을 구동했었다. 

여태 로컬에서만 개발을 해 왔지만, 이제는 서버를 이용해서 Atlas에 DB를 전해주고, 받아오는 일을 할 것이다.

 

데이터 통신 방식

데이터 통신을 할 때도 방식에 따라서 업무가 달라진다.

HTTP통신의 request method에 따라 많은 요청 종류가 있지만, 여기서는 단 2개의 핵심 방식을 가지고 진행한다.

HTTP 통신에 대해서는 나중에 따로 포스팅 할래

GET

  • 데이터를 조회할 때 사용
  • 데이터 전달은 ?key=value의 형태로 전달한다. 

아델의 hello를 구글에 검색했을 때의 주소창을 보면, ?q=hello 형태로 데이터를 받아오는것을 볼 수 있다.

POST

  • 데이터를 생성, 변경, 삭제할때 사용
  • 보통 body에 key:value형태로 숨겨서 전달

GET 요청의 코드 뜯어보기

프론트단의 function 안에 들어가는 ajax 코드이다.

button의 onclick에 function이 연동되어 있고, 그 function 안에 이 ajax 코드가 들어있다고 생각해보자.

$.ajax({
    type: "GET",
#GET 방식으로 가져올거야
    url: "/test?title_give=봄날은간다",
#/test라는 창구에서 title_give로 '봄날은간다'를 가져 갈게 (/id?주민등록번호=990123-1234567)과 비슷한 내용
    data: {},
    success: function(response){
#잘 된다면 내가 response에 받은 것들로 이러이러한 것들을 해볼게
       console.log(response)
    }
  })
#이 '봄날은간다'는 추후에 python의 콘솔창에 찍히게 된다.

그러면 이제 서버단으로 넘어와서, app.py에 추가작업을 해 주어야 한다.

@app.route('/test', methods=['GET'])
#/test라는 창구에서 get방식으로 무엇을 해볼게! 
def test_get():
   title_receive = request.args.get('title_give')
#title_give라는 이름으로 무엇인가를 받아와서 title_receive라는 변수에 넣고
   print(title_receive)
   return jsonify({'result':'success', 'msg': '이 요청은 GET!'})
#그러면 json 방식으로 {'result':'success', 'msg': '이 요청은 GET!'}를 response로 넘겨줄게!

다시 설명해보자면,

  1. button을 누르는 순간 함수가 동작하여 그 안에 있는 ajax코드가 실행된다.
  2. ajax 코드는 get 방식으로, 아규먼트인 title_give로 '봄날은 간다'를 가져간다.
  3. 그 '봄날은 간다'는 app.py 안의 함수인 test_get의 'title_give'에 들어가고, title_receive 변수 안에 저장된다.
  4. 그리고 print함수로 (title_receive)를 찍기 때문에 파이썬 콘솔에 '봄날은 간다'가 찍힌다.
  5. 문제가 없었으니 다시 클라이언트 단으로 넘어가서 response에 {'result' : 'success', 'msg' :'이 요청은 get'}가 저장되고
  6. console.log로 인해 브라우저의 콘솔 창에 json형식의 response가 찍힌다.

POST 요청의 코드 뜯어보기

프론트단의 function 안에 들어가는 ajax 코드이다.

button의 onclick에 function이 연동되어 있고, 그 function 안에 이 ajax 코드가 들어있다고 생각해보자.

$.ajax({
    type: "POST",
    url: "/test",
    data: { title_give:'봄날은간다' },
 #/test의 url 안에있는 request로 봄날은간다를 가져간다.
    success: function(response){
 #다시 response로 돌아온 json형식의 텍스트를 받아서
       console.log(response)
 #브라우저의 콘솔에 찍는다.
    }
  })

app.py

@app.route('/test', methods=['POST'])
def test_post():
   title_receive = request.form['title_give']
#그래서 title_give에 있는 '봄날은 간다'를 가져와서 title_receive에 넣는다.
   print(title_receive)
#그러면 콘솔에 '봄날은 간다'가 찍히고,
   return jsonify({'result':'success', 'msg': '이 요청은 POST!'})
#json 형식으로 {'result':'success', 'msg': '이 요청은 POST!'}를 response에 전달한다.

순서

  1. button을 누르는 순간 onclick이 동작해서 function를 실행한다.
  2. /test 안에 있는 '봄날은간다'가 request body를 통해 'title_give'에 저장된다.
  3. 저장된 title_give가 app.py로 건너와서 title_receive 안에 담기고, print 메서드를 통해 파이썬 콘솔에 찍힌다.
  4. 그리고나서 json형태에 담긴 파일이 response를 통해 다시 ajax에 전달되고,
  5. 오류가 없었으므로 response를 아규먼트로 갖는 function이 동작하고, 그 안에 있는 console.log 메서드를 통해
  6. 브라우저의 콘솔에 json이 찍힌다.

 

 

주의할 점

/test를 은행 창구에서 동시에 다른 방식으로 찾아갈 수는 없다.

POST 방식의 메소드가 2개가 붙을 수 없다.

DB는 왜 쓸까?

내 방 구조를 바꿀 때에는 크게 두가지 일로 나뉜다. 방을 꾸밀 것인가? 방을 정리할 것인가?

방 정리는 왜 할까? 내가 필요한 물건을 나중에 잘 찾기 위해 정리해야 한다.

그렇지 않다면 내 옷이 수백 벌이라고 생각했을 때 나가기 위해 옷 하나 찾는다고 해도 엄청난 시간이 소요될 것

데이터도 하나의 옷/책과 같아서 이를 정리해두어야 나중에 데이터를 쉽고 빠르게 꺼내 쓸 수 있다.

 

DB의 종류

DB는 크게 두가지로 나눌 수 있다.

RDBMS(SQL : Structured Query Language) -> 관계형 데이터베이스, 빡빡하지만 나중에 꺼내쓰기 편하다

No-SQL(Not only SQL) -> 딕셔너리 형태로 저장되어 있으며 저장하기에 유하지만 나중에 꼬이면 매우 힘들다

RDB에는 MySql, Oracle 등이 있고, No-SQL에는 MongoDB등이 있다.

어떤 데이터베이스는 RDB가 좋고 어떤 데이터는 NoSQL이 좋으니, 잘 골라서 써야 한다.

 

MongoDB

몽고DB는 NoSql의 대표주자로, 전체 DB 사용 랭킹에서는 5위 (2023.1 기준), NoSQL중에서는 압도적 1위를 달리고 있다.

출처 https://db-engines.com/en/ranking

몽고DB도 아틀라스를 통해 클라우드 형태로 DB를 제공해주고 있는데, 설치 및 사용도 간편한 축에 속한다.

 

주의할 점 및 실행

왼쪽 메뉴에서 Network Access를 열어 꼭 IP를 추가해야한다.

이렇게 해주지 않으면 도메인에서 들어오는 값이 저장되지 않는다.

이렇게 연결하고 파이썬에서 dnspython과 pymongo 패키지를 설치해준 후

from pymongo import MongoClient
client = MongoClient('여기에 URL 입력')
db = client.db'name 입력'

위와같은 코드를 써주고 insert, update, find 등의 코드로 확인해주면 된다.

패키지란?

모듈을 모아 놓은 단위로, 패키지의 모음은 라이브러리로 볼 수 있다.

이런 패키지를 돌리는 하나의 격리된 실행 환경을 가상 환경이라고 한다.

 

request 패키지

일종의 Ajax 역할을 하는 패키지로, API 데이터를 추출할 때 사용되며

대개 크롤링/스크래핑을 할 때 많이 사용한다.

사용 예

import requests # requests 라이브러리 설치 필요

r = requests.get('http://spartacodingclub.shop/sparta_api/seoulair')
rjson = r.json()

gus = rjson['RealtimeCityAir']['row']

for gu in gus:
	if gu['IDEX_MVL'] < 60:
		print (gu['MSRSTE_NM'], gu['IDEX_MVL'])

1. request 패키지의 get 함수를 사용해서 api 정보를 받아온다.

2. 받아온 api 정보를 json형식으로 파싱한다.

3. 파싱된 정보에서 내가 원하는 값을 추출하고 돌린다.

 

beautifulsoup 패키지

html 파싱을 통해 필요한 부분이 어디에 있는지를 파이썬 콘솔에서 찾고,

브라우저의 콘솔창에서 카피셀렉터를 통해 정확한 위치를 찾은 뒤, split으로 잘라서

soup.select 등으로 필요한 부분만 가져오면 된다.

사용 예

# 선택자를 사용하는 방법 (copy selector)
soup.select('태그명')
soup.select('.클래스명')
soup.select('#아이디명')

soup.select('상위태그명 > 하위태그명 > 하위태그명')
soup.select('상위태그명.클래스명 > 하위태그명.클래스명')

# 태그와 속성값으로 찾는 방법
soup.select('태그명[속성="값"]')

# 한 개만 가져오고 싶은 경우
soup.select_one('위와 동일')

그래서 결국 어떻게 원하는걸 가져오느냐?

카피셀렉터를 통해 만약 찾아낸 title의 주소가 old_content>table>tbody>tr:~~~ > td.title>div>a일때

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.naver?sel=pnt&date=20210829',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movies = soup.select('#old_content > table > tbody > tr')

for movie in movies:
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        print (a_tag.text)

이렇게 영화 제목만을 딸 수 있다.

수업 목표

  1. Flask 프레임워크를 활용해서 API를 만들 수 있다.
  2. '버킷리스트'를 완성한다.
  3. EC2에 내 프로젝트를 올리고, 자랑한다!

개인적인 마음가짐

프레임워크를 활용해서 API를 만들 수 있다는 것은 쉽다면 쉽고 어렵다면 어려운 작업이다.

물론 배우는것에는 4주차에 이어서 조금 더 유기적인 활동을 가진 코드들을 짤 것인데, 여기서 한층 더 유기적인 작업이 되기 위해서는 어떻게 조금 더 홈페이지를 꾸며야 할 지에 대해 생각해봐야 할 것이다.

EC2같은경우는 남궁성의 스프링의 정석을 들을 때 EC2의 원리에 대해 간단하게 들어보았지만, git bash를 이용해 nohup를 쓰거나 og태그를 꾸며주는것에 대해서는 해보지 못해서 이 부분에 대해서 자세하게 들어봐야 할 것 같다.

 

배운것들

https://choincnp.tistory.com/16

 

항해 웹개발 종합반 5주차, 서버 연습(3)

세 번째, 버킷 리스트 STEP 1 : 무엇을 고려해야 하는가? URL은 어떻게 만들 것인지, 요청 방식은 어떻게 될 것인지? 클라이언트에서 서버로는 어떤 것을 넘겨 줄 것인지? 서버에서 클라이언트로는

choincnp.tistory.com

https://choincnp.tistory.com/17

 

항해 웹개발 종합반 5주차, 서버 구동

웹 서비스를 런칭하기 위해선... 언제나 요청에 응답할 수 있어야 한다. (컴퓨터가 항상 실행 중이어야 한다) '모두가 접근할 수 있는' 공개 IP주소로 내 서비스에 접근할 수 있어야 한다. 서버는

choincnp.tistory.com

 

느낀점

정말 너무너무 기본적인것들을 배웠다. HTTP의 원리에 대해서 너무 딱딱하게 이해만 하고 있던 나에게는 좋은 실습 시간이 되었고, 각 프레임워크별로 서버를 세팅하고 유지하는 방법이 다 다르다는것을 느꼈다. Spring같은 경우는 동적 페이지가 아닌 정적 페이지를 수정하는 경우에만(이번 프로젝트에서는 templates 디렉토리에 관련된 파일) 서버 재시작을 하지 않고도 바로바로 표시가 되었는데, flask같은 경우에는 app.py파일에서 논리적인 issue만 있지 않으면 굳이 재시작을 하지 않아도 됐었다. 예를 들어 (location = )이라고만 써놓고 한참동안 고민하는 경우에는 당연히 오류로 컴파일되어 서버가 자동으로 다운된다.

git bash같은 경우에는 처음에는 누가 cd 입력하는것만 봐도 멋들어지면서도 울렁증이 왔었는데, 조금씩 해 보니까 익숙해지면서도 그래도 아직 멀미가 난다. 조금씩 더 써봐야 할 것 같다.

도메인은 예전에 부동산 일을 할 때 도메인을 사서 연결해본적이 있어서 다시 복습하는 것 같아 재미있었다.

+ Recent posts