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

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

수업 목표

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

1. 무비스타


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

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

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

    1) 데이터 스크래핑

    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
    
      import requests
      from bs4 import BeautifulSoup
    
      from pymongo import MongoClient
    
      client = MongoClient('localhost', 27017)
    
      # 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/'
      db = client.dbsparta
                  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
1
2
3
4
5
    @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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    $(document).ready(function () {
       // 새로고침하고 'list 연결되었습니다!' 가 alert 창에 나오면 연결 성공
        showStar(); 
    });

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

2) 서버부터 만들기

1
2
3
4
5
    @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) 클라이언트 만들기

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
    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="#" 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>
                                                </div>`
                                $('#star-box').append(temp_html)
                            }
                        }
                    });
                }

4) 완성 확인하기

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

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

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

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

  • 서버 코드 : app.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
       @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
1
2
3
4
5
6
7
8
9
10
11
    function likeStar(name) {
        $.ajax({
            type: 'POST',
            url: '/api/like',
            data: {name_give:name},
            success: function (response) {
                alert(response['msg']);
                window.location.reload()
            }
        });
    }

2) 서버부터 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @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) 클라이언트 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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
    1
    2
    3
    4
    5
    
      @app.route('/api/delete', methods=['POST'])
      def delete_star():
          sample_receive = request.form['sample_give']
          print(sample_receive)
          return jsonify({'msg': 'delete 연결되었습니다!'})
    
  • 클라이언트 코드 : index.html
1
2
3
4
5
6
7
8
9
10
    function deleteStar(name) {
        $.ajax({
            type: 'POST',
            url: '/api/delete',
            data: {sample_give:'샘플데이터'},
            success: function (response) {
                alert(response['msg']);
            }
        });
    }

2) 서버부터 만들기

1
2
3
4
5
6
7
8
    @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) 클라이언트 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // 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가 없어서 이를 대체하는 프로그램)
    1
    2
    3
    4
    
      	ssh -i 받은키페어를끌어다놓기 ubuntu@AWS에적힌내아이피
    	
      	예) 아래와 비슷한 생김새!
      ssh -i /path/my-key-pair.pem ubuntu@13.125.250.20
    
  • 기본 리눅스 언어(일종의 마우스 역할)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      ls: 내 위치의 모든 파일을 보여준다.
    
      pwd: 내 위치(폴더의 경로)를 알려준다.
    
      mkdir: 내 위치 아래에 폴더를 하나 만든다.
    
      cd [갈 곳]: 나를 [갈 곳] 폴더로 이동시킨다.
    
      cd .. : 나를 상위 폴더로 이동시킨다.
    
      cp -r [복사할 것] [붙여넣기 할 것]: 복사 붙여넣기
    
      rm -rf [지울 것]: 지우기
    
      sudo [실행 할 명령어]: 명령어를 관리자 권한으로 실행한다.
      sudo su: 관리가 권한으로 들어간다. (나올 때는 exit으로 나옴)
    

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

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

    6) nohup 설정

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

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

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

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

    8) og 태그 설정

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

3. 최종 프로젝트 완성본


링크 : http://kimcno3.shop/

This post is licensed under CC BY 4.0 by the author.