스파르타 코딩클럽 개발일지_5주차

김선호·2021년 10월 12일
0

수업 목표
1. Flask 프레임워크를 활용해서 API를 만들 수 있다.
2. '마이 페이보릿 무비스타'를 완성한다.
3. EC2에 내 프로젝트를 올리고, 자랑한다!

1. 무비스타

  • 좋아요 많은 순으로 영화배우의 이름, 사진, 소개url, 최근 작품 내용 게시
  • 좋아요를 누르면 숫자 1 추가
  • 삭제하기를 누르면 해당 영화배우 목록은 삭제

1-1 DB 만들기(데이터쌓기)

  • 필요한 영화배우의 데이터를 네이버 영화 페이지에서 크롤링

1) 데이터 스크래핑

    import requests
    from bs4 import BeautifulSoup

    from pymongo import MongoClient

    client = MongoClient('localhost', 27017)
    db = client.dbsparta

    # DB에 저장할 영화인들의 출처 url을 가져옵니다.
    def get_urls():
        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/rpeople.nhn', headers=headers)

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

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

        urls = []
        for tr in trs:
            a = tr.select_one('td.title > a')
            if a is not None:
                base_url = 'https://movie.naver.com/'
                url = base_url + a['href']
                urls.append(url)

        return urls

    # 출처 url로부터 영화인들의 사진, 이름, 최근작 정보를 가져오고 mystar 콜렉션에 저장합니다.
    # 'insert_all' 함수에서 같은 이름으로 사용하게 위해 url을 변수로 지정
    def insert_star(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')

        name = soup.select_one('#content > div.article > div.mv_info_area > div.mv_info.character > h3 > a').text
        img_url = soup.select_one('#content > div.article > div.mv_info_area > div.poster > img')['src']
        recent_work = soup.select_one(
            '#content > div.article > div.mv_info_area > div.mv_info.character > dl > dd > a:nth-child(1)').text

        doc = {
            'name': name,
            'img_url': img_url,
            'recent': recent_work,
            'url': url,
            'like': 0
        }

        db.mystar.insert_one(doc)
        print('완료!', name)

    # 기존 mystar 콜렉션을 삭제하고, 출처 url들을 가져온 후, 크롤링하여 DB에 저장합니다.
    def insert_all():
        db.mystar.drop()  # mystar 콜렉션을 모두 지워줍니다.
        urls = get_urls()
        for url in urls:
            insert_star(url)

    ### 실행하기
    insert_all()

2) 완성 확인하기

  • mongoDB에 저장된 데이터들

1-2 GET 연습(보여주기)

  • DB에 저장된 영화배우 정보를 계획에 맞게 게시하는 기능 추가

1) 클라이언트와 서버 확인하기

  • 서버 코드 : app.py
    @app.route('/api/list', methods=['GET'])
    def show_stars():
        sample_receive = request.args.get('sample_give')
        print(sample_receive)
        return jsonify({'msg': 'list 연결되었습니다!'})
  • 클라이언트 코드 : index.html
    $(document).ready(function () {
       // 새로고침하고 'list 연결되었습니다!' 가 alert 창에 나오면 연결 성공
        showStar(); 
    });

    function showStar() {
          $.ajax({
              type: 'GET',
              url: '/api/list?sample_give=샘플데이터',
              data: {},
              success: function (response) {
                  alert(response['msg']);
              }
          });
      }

2) 서버부터 만들기

    @app.route('/api/list', methods=['GET'])
    def show_stars():
        stars = list(db.mystar.find({}, {'_id': False}).sort("like", -1)) 
        # .sort() : like 값을 역순으로 정렬하라는 함수
        return jsonify({'stars': stars})

3) 클라이언트 만들기

    function showStar() {
                    $.ajax({
                        type: 'GET',
                        url: '/api/list',
                        data: {},
                        success: function (response) {
                            let stars = response['stars']
                            for (let i = 0; i <stars.length; i++) {
                                let img_url = stars[i]['img_url']
                                let like = stars[i]['like']
                                let name = stars[i]['name']
                                let recent = stars[i]['recent']
                                let url = stars[i]['url']
								// 배우 소개박스 코드에 DB로부터 받아온 데이터를 대입
                                let temp_html = `<div class="card" id="star_media">
                                                    <div class="card-content" >
                                                        <div class="media">
                                                            <div class="media-left">
                                                                <figure class="image is-48x48">
                                                                    <img
                                                                            src="${img_url}"
                                                                            alt="Placeholder image"
                                                                    />
                                                                </figure>
                                                            </div>
                                                            <div class="media-content">
                                                                <a href="${url}" target="_blank" class="star-name title is-4">${name}(좋아요:
                                                                    ${like})</a>
                                                                <p class="subtitle is-6">${recent}</p>
                                                            </div>
                                                        </div>
                                                    </div>
                                                    <footer class="card-footer">
                                                        <a href="#"token interpolation">${name}')" class="card-footer-item has-text-info">
                                                            위로!<span class="icon"><i class="fas fa-thumbs-up"></i></span>
                                                        </a>
                                                        <a href="#"token interpolation">${name}')" class="card-footer-item has-text-danger">
                                                            삭제<span class="icon"><i class="fas fa-ban"></i></span>
                                                        </a>
                                                    </footer>
                                                </div>`
                                $('#star-box').append(temp_html)
                            }
                        }
                    });
                }

4) 완성 확인하기

  • 나열되는 영화배우 리스트

1-3 POST 연습(좋아요 +1)

  • 좋아요를 누르면 좋아요 개수가 추가(DB 내 'Like' +1)

1) 클라이언트와 서버 확인하기

  • 서버 코드 : app.py
     @app.route('/api/like', methods=['POST'])
    def like_star():
        name_receive = request.form['name_give']

        target_star = db.mystar.find_one({'name': name_receive})
        current_like = target_star['like']

        new_like = current_like + 1

        db.mystar.update_one({'name': name_receive}, {'$set': {'like': new_like}})

        return jsonify({'msg': '좋아요 완료!'})
  • 클라이언트 코드 : index.html
    function likeStar(name) {
        $.ajax({
            type: 'POST',
            url: '/api/like',
            data: {name_give:name},
            success: function (response) {
                alert(response['msg']);
                window.location.reload()
            }
        });
    }

2) 서버부터 만들기

    @app.route('/api/like', methods=['POST'])
    def like_star():
        name_receive = request.form['name_give']

	# 좋아요를 누른 영화배우를 DB에서 가져온다.
        target_star = db.mystar.find_one({'name': name_receive})
    	# 현재 좋아요 수를 가져온다.
        current_like = target_star['like']
    	# 현재 좋아요 수에 +1을 해준다.
        new_like = current_like + 1
    	# 추가한 좋아요 수를 DB에 업데이트한다.
        db.mystar.update_one({'name': name_receive}, {'$set': {'like': new_like}})

        return jsonify({'msg': '좋아요 완료!'})

3) 클라이언트 만들기

// temp_html에 포함된 좋아요, 삭제하기 버튼 코드 , DB에서 가져온 name값을 변수로 받는다.
<footer class="card-footer">
  <a href="#" onclick="likeStar('${name}')" class="card-footer-item has-text-info">위로!<span class="icon"><i class="fas fa-thumbs-up"></i></span> </a>
  <a href="#" onclick="deleteStar('${name}')" class="card-footer-item has-text-danger">삭제<span class="icon"><i class="fas fa-ban"></i></span></a>
</footer>function likeStar(name) {
                    $.ajax({
                        type: 'POST',
                        url: '/api/like',
                      // name 변수에 들어온 값을 name_give 라는 이름으로 서버에 전송 , 전송이 잘 되면(success) 서버로부터 return 값을 response 이름으로 받아와 함수 실행
                        data: {name_give:name}, 
                      // 서버에서 제대로 데이터를 받았다면 success 실행, 서버로부터 return 값을 response 이름으로 받아와 함수 실행
                        success: function (response) { 
                            alert(response['msg']); // 메세지값 alert
                            window.location.reload(); // 페이지 리로드

                        }
                    });
     }

4) 완성 확인하기

  • 정상적으로 작동했다는 alert 메세지
  • 추가된 좋아요 수와 좋아요 순으로 재정렬되는 리스트

1-4 POST 연습(삭제하기)

  • 삭제하기를 누르면 해당 영화배우 데이터 전부 삭제(drop)

1) 클라이언트와 서버 확인하기

  • 서버 코드 : app.py
    @app.route('/api/delete', methods=['POST'])
    def delete_star():
        sample_receive = request.form['sample_give']
        print(sample_receive)
        return jsonify({'msg': 'delete 연결되었습니다!'})
  • 클라이언트 코드 : index.html
    function deleteStar(name) {
        $.ajax({
            type: 'POST',
            url: '/api/delete',
            data: {sample_give:'샘플데이터'},
            success: function (response) {
                alert(response['msg']);
            }
        });
    }

2) 서버부터 만들기

    @app.route('/api/delete', methods=['POST'])
    def delete_star():
    	# 브라우저에서 보내준 데이터(post방식은 data 딕셔너리에 입력)를 받아와 변수에 담는다
        name_receive = request.form['name_give'] 
        # DB에 저장
        db.mystar.delete_one({'name': name_receive}) 
        # 위 과정이 완료되면 return값 전송
        return jsonify({'msg': '삭제되었습니다!!'})

3) 클라이언트 만들기

    // temp_html에 포함된 좋아요, 삭제하기 버튼 코드 , DB에서 가져온 name값을 변수로 받는다.
    <footer class="card-footer">
      <a href="#" onclick="likeStar('${name}')" class="card-footer-item has-text-info">위로!<span class="icon"><i class="fas fa-thumbs-up"></i></span> </a>
      <a href="#" onclick="deleteStar('${name}')" class="card-footer-item has-text-danger">삭제<span class="icon"><i class="fas fa-ban"></i></span></a>
    </footer>function deleteStar(name) {
          $.ajax({
              type: 'POST',
              url: '/api/delete',
              data: {name_give:name},
              success: function (response) {
                  alert(response['msg']);
                  window.location.reload()
              }
          });
      }

4) 완성 확인하기

  • 삭제버튼 클릭 후 정상적으로 처리되었다는 alert 창
  • 재로딩 후 삭제된 배우 리스트(캐리 후쿠나가)

2. 웹 서비스 런칭

2-1 웹 서비스 런칭 기본 개념

  • 웹 서비스 런칭 : 누구나 내 서비스를 이용할 수 있도록 하기 위한 작업
  • 즉, 언제나 서버가 클라이언트의 요청에 응답해 줄 준비가 되어있어야 한다.
  • 언제나 요청에 응답하려면, 아래 두가지 조건이 성립되어야 한다.
    1) 컴퓨터가 항상 켜져있고 프로그램이 실행
    2) 모두가 접근할 수 있는 공개 주소인 공개 IP 주소(Public IP Address)로 나의 웹 서비스에 접근 가능한 상태
  • 우리는 AWS 라는 클라우드 서비스에서 편하게 서버를 관리하기 위해서 항상 켜 놓을 수 있는 컴퓨터인 EC2 사용권을 구입해 서버로 사용할 것이다.

2-2 웹 서비스 런칭 절차 및 세부 내용

1) AWS EC2서버 구매

  • EC2서버란?
    : Amazon Elastic Compute Cloud(Amazon EC2)의 약자로 Amazon Web Services(AWS) 클라우드에서 확장 가능 컴퓨팅 용량을 제공하는 것을 의미

  • EC2를 구매하면 가상 컴퓨팅 기술을 통해 원격 서버를 사용할 수 있다.(구매절차 생략)

2) EC2 서버에 접속

  • 접속 전, SSH(Secure Shell Protocol)란?
    • 다른 컴퓨터에 접속할 때 쓰는 프로그램으로 다른 것들 보다 보안이 상대적으로 뛰어나다.
    • 접속할 컴퓨터의 22번 포트가 열려있어야 접속 가능하며 AWS EC2의 경우, 이미 22번 포트를 열어놓는다.
  • gitbash(Window의 경우 SSH가 없어서 이를 대체하는 프로그램)
    	ssh -i 받은키페어를끌어다놓기 ubuntu@AWS에적힌내아이피
	
    	예) 아래와 비슷한 생김새!
	ssh -i /path/my-key-pair.pem ubuntu@13.125.250.20
  • 기본 리눅스 언어(일종의 마우스 역할)
    ls: 내 위치의 모든 파일을 보여준다.

    pwd: 내 위치(폴더의 경로)를 알려준다.

    mkdir: 내 위치 아래에 폴더를 하나 만든다.

    cd [갈 곳]: 나를 [갈 곳] 폴더로 이동시킨다.

    cd .. : 나를 상위 폴더로 이동시킨다.

    cp -r [복사할 것] [붙여넣기 할 것]: 복사 붙여넣기

    rm -rf [지울 것]: 지우기

    sudo [실행 할 명령어]: 명령어를 관리자 권한으로 실행한다.
    sudo su: 관리가 권한으로 들어간다. (나올때는 exit으로 나옴)

3) 서버 환경 통일 (initial 파일 스크랩 하고 코드별 설명)

    # UTC to KST : 한국시간 세팅 
    sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

    # python3 -> python : python3 명령어를 python으로 변경
    sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10

    # pip3 -> pip : pip3 명령어를 pip으로 변경
    sudo apt-get update
    sudo apt-get install -y python3-pip
    pip3 --version
    sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

    # port forwarding : http 기본 포트인 80포트로 요청이 들어오면 5000포트로 자동으로 넘겨주는 개념
    # 'IP주소:5000'에서 ':5000'을 생략시키기 위한 작업
    sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000

    # MongoDB - install : MongoDB를 원격 서버에 설치
    wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
    echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
    sudo apt-get update
    sudo apt-get install -y mongodb-org

    sudo mkdir -p /data/db

    # MongoDB - run : MongoDB 실행
    sudo service mongod start
    sleep 7
    netstat -tnlp

    # MongoDB set user, set conf file : mongoDB 접속 계정 생성하기
    # admin으로 계정 변경 및 계정 생성(ID,PW : test)하기
    mongo admin --eval 'db.createUser({user: "test", pwd: "test", roles:["root"]});'
    sudo sh -c 'echo "security:\n  authorization: enabled" >> /etc/mongod.conf'
    sudo sed -i "s,\\(^[[:blank:]]*bindIp:\\) .*,\\1 0.0.0.0," /etc/mongod.conf

    sudo service mongod stop
    sudo service mongod start
    sleep 5
    netstat -tnlp
    
    # flask 패키지 설치 : app.py 실행을 위해 필수적
    pip install flask

4) AWS에서 포트 열여주기

  • EC2 서버(=가상의 내 컴퓨터)에서 포트를 따로 설정하는 것 외에도, AWS EC2에서도 자체적으로 포트를 열고/닫을 수 있게 관리를 할 수 있다.
    → 그래서 AWS EC2 Security Group에서 인바운드 요청 포트를 열어줘야 서버로 접속이 가능하다.
  • EC2 Security Group 에서 아래 3개의 포트 추가
    • 80포트(기본 http 포트)
    • 5000포트(flask 기본포트)
    • 27017포트(외부에서 mongoDB 접속을 하기 위한 포트)

5) 원페이지 쇼핑몰 업로드하기

  • mongoDB 명령어 수정
	# 기존 명령어 : localhost로 접속
	client = MongoClient('localhost', 27017)

	# 수정한 명령어 : 원격 서버로 접속
	# 아이디 : test , 비밀번호 : test
	client = MongoClient('mongodb://아이디:비밀번호@localhost', 27017)
  • pymongo 패키지 설치
  # 설치하기
  pip install pymongo
  • app.py 실행
  python app.py 
  • 브라우저에서 접속
  http://내AWS아이피:5000/

6) nohup 설정

  • 내가 SSH 접속을 끊었을 때 다른 사람이 내가 만든 사이트에 접속할 수 없다면 제대로 된 웹 서비스 런칭이라 볼 수 없다. 이를 해결하기 위한 방법이 nohup 설정을 하는 것이다.

  • nohup 설정 명령어

    • 원격 접속을 종료하더라도 서버가 계속 돌아가게 하기
        # 아래의 명령어로 실행하면 된다
        nohup python app.py &
    • 서버 종료하기 - 강제종료하는 방법
        # 아래 명령어로 미리 pid 값(프로세스 번호)을 본다
        ps -ef | grep 'app.py'
    
        # 아래 명령어로 특정 프로세스를 죽인다
        # pid값 : 5자리 숫자조합 중 앞에 두가지 값
        kill -9 [pid값] 
    • 다시 켜기
        nohup python app.py &

7) 도메인 구입(가비아)

  • IP 주소
    : 컴퓨터가 통신할 수 있도록 컴퓨터마다 가지는 숫자로 구성된 고유한 주소
  • 도메인(Domain)
    : 사람이 알아보기 쉽게 IP주소를 알파벳으로 바꾼 주소
    도메인 & IP
  • 가비아에서 구입한 url(kimcno3.shop)을 EC2서버 IP와 연결하면 완료!!

8) og 태그 설정

  • 프로젝트를 sns에 공유했을 때 나오는 간략한 설명 혹은 이미지를 og태그라고 한다.
  • og태그 설정 방법
    // head 태그 안에 아래 태그를 추가, content 값을 원하는 대로 수정
    // img 파일은 static 폴더 안에 저장(800X400 사이즈)
    <meta property="og:title" content="나만의 쇼핑몰" />
    <meta property="og:description" content="맛있는 사과사세요~~🍎" />
    <meta property="og:image" content="{{ url_for('static', filename='ogimage.png') }}" />
  • 카카오톡으로 공유한 프로젝트 모습

3. 최종 프로젝트 완성본

링크 : http://kimcno3.shop/

0개의 댓글