[해커톤] 웹 개발 팁 정리

2023. 4. 2. 01:35Tip

728x90

해커톤으로 배포까지 진행한 과정 중 팁 정리

Step 1: 프론트 기초 (index.html  작성)

더보기

* Code alignment in Visual Studio Code IDE *

  1. WIndows: [Alt + Shift + F]
  2. Mac: [Shift + Option + F]
  3. 둘 다 손가락 형태가 비슷하게 나온다. 까먹었으면 [오른쪽 클릭 > 문서 서식]의 단축키 참고.

* Import Google Fonts *

  1. Visit Browse Fonts - Google Fonts
  2. Choose a font style
  3. Follow the 'Use on the web' in the drawer box

 

 

* Background style setting *

background-color: url('');
background-position: center;
background-size: cover;

 

 

* Make Image darker *

<style>
.img{ filter: brightness(0.5) }
div{ opacity: 0.5; }
.doo{ background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url(''); }
</style>

 

* Center alignment tag <div> itself *

<style>
div[id='headline'] { margin: 0 auto 0 auto; }
</style>

 

* Center alignment of child contents in <div> *

  1. display: flex;
  2. flex-direction: column;
  3. align-items: center;
  4. justify-content: center;
  5. CSS의 justify-content 속성은 flex container의 main-axis(수평축)을 기준으로 content item들 사이의 간격을 어떻게 배치할지를 정합니다. justify-content - CSS: Cascading Style Sheets | MDN (mozilla.org)
  6. CSS의 align-items 속성은 flex container의 cross-axis(수직축)을 기준으로 content item들을 어떻게 배치할지를 정합니다. align-items - CSS: Cascading Style Sheets | MDN (mozilla.org)

Step 2: JQuery Selector를 이용한 정적 웹페이지 구현 

더보기

* Using JQuery *

  • JavaScript를 이용한 DOM Tree 직접 제어 문법을 단순화

예전에는 인기가 많았으나, 최근에는 Lodash, Moment에 밀리는 추세

  • CSS 선택자 엔진 Sizzle을 통해 손쉬운 엘리먼트 선택 가능
  • JQuery.ajax() 메소드를 통한 API 접근성 등 인기가 많았음... BUT
  1. 웹 표준의 발전으로 querySelector, Fetch API 등장
  2. 개발이 편한 크롬 브라우저의 등장
  3. 최신 트렌드인 Interactive Web은 DOM을 직접 조작하는 옛날 방식으로는 렌더링 성능 구림.
  4. 대신, 메모리에 Virtual DOM을 구성하여 실제 돔과의 차이만을 반영하는 React, Vue.js 프레임워크 각광.

이러한 한계를 극복하고 경쟁력을 갖기 위해

  1. JavaScript 초경량 (매우 작은 용량) 라이브러리로 경쟁력 확보
  2. 방법 1: 최신 버전 브라우저만 지원
  3. 방법 2: Sizzle은 최소한의 필수 기능만
  4. 브라우저 호환 기능: 브라우저 의존적인 기능들을 상호 호환 가능하도록

결론: 정적이고 가벼운 웹페이지를 신속하게 제작하는 용도로 매우 적합하다.

 

* How to use JQuery Selector Sizzle *

  • All / ID / Tag / class / multi Selector
  • : Filter Selector
  • Attribute Contains Prefix Selector... 등

결론: 되게 많은데 그냥 querySelector(All)하고 regex를 익히는 게 훨씬 나음

Filter는 생각나면 나중에 써보기

Reference: 1. https://hello-bryan.tistory.com/241, 2. https://api.jquery.com/category/selectors/

Step 3: Web Scraping(Python), MongoDB(NoSQL)

더보기

* Web Scraping using library bs4 *

  • URL을 매개변수로 BeautifulSoup 객체를 만드는 함수를 작성해봤다.
# init bs4
import requests
from bs4 import BeautifulSoup

def get_soup_by_url_from_bs4(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)
  return BeautifulSoup(data.text, 'html.parser')

soup = get_soup_by_url_from_bs4('https://bit.ly/42KfFIc')

 

* pprint *

  • 리스트를 예쁘게 출력하기 위해 pprint(PrettyPrinter) 라이브러리를 사용했다.
# Pretty Printer
import pprint
pp = pprint.PrettyPrinter()

 

* MongoDB *

  • DB명을 바탕으로 MongoClient 객체를 만드는 함수를 작성해봤다.
# init pymongo
import os
from dotenv import load_dotenv
from pymongo import MongoClient

def get_DB_by_DBname_from_pymongo(DBname):
    load_dotenv()
    root_id = os.environ.get('root_id')
    root_pw = os.environ.get('root_pw')
    client = MongoClient(
        f"mongodb+srv://{root_id}:{root_pw}@my-mongo.fnaorrz.mongodb.net/?retryWrites=true&w=majority")
    return client[f"{DBname}"]

db = get_DB_by_DBname_from_pymongo('test')
소스에 관리자 정보가 명시되어 있는 것이 싫어서 .env 파일로 감추었다.

 

 

? ? ? 과연 MongoDB는 어떤 방식으로 웹과 연동되는가 ? ? ?

MongoDB를 사용하면 웹에서도 연동이 되니, 결국 HTTP(S) request를 보낸다는 것인데... 어떤 방식으로 보내는지 이해가 가지 않는다.

대충 사용자 메모리에 적재해두고 백그라운드로 전송하는 방식인 것 같다. (https://ryu-e.tistory.com/2)

연결이 이루어지는 MongoClient 클래스의 생성자를 뜯어봤다.

일단 localhost:27017로 보내는건가? 그럼 DB에 업데이트 쿼리를 전송하고, 통신을 어떻게 하는지 봐야겠다.

Wireshark 필터링 결과
US Server로 가는군

내부적으로 Query나 데이터를 TLSv1.2 프로토콜로 암호화하고, Mongo Wire Protocol로 미국 서버에 보내는 것 같다. 잘 받았다고 ACK 오면서 Data를 함께 보내는 건 DB의 무결성 검증을 위한 통신으로 유추되지만, document에서도 정확한 원리를 찾을 수 없었다.

(https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/)

Wireshark를 통해 분석했고, TLSv1.2 프로토콜은 443번 포트에서 통신이 이루어지므로,

udp || tcp.port == 443 || tcp.port == 27017 를 필터로 잡고 분석하면 되겠지만 정보들이 암호화 되어 있어 볼 수 없다.

 

기술 매니저님께 여쭤보니, 하드 코딩 당연히 안 되고, 실무에서는 .env보다는 배포를 지원하는 플랫폼(Github, docker 등)에서의 내장 기능을 활용한다고 한다.

 

찾아보니 requests 모듈 내 HTTPS URL은 통신 간 자동적으로 암호화가 진행된다.

 

참고 링크

Step 4: 프로젝트 수행 방식 정리

더보기

* 프로젝트 수행 절차 *

1. 프로젝트 정의: 어떤 기능을 수행하는지

2. API 설계: 기능 구현을 Server/Client의 콜백 관계로 표현

3. API 명세: (비동기적 구현을 가정하고) 각 콜백의 역할을 정의하고, 참조하는 데이터 명시 

  - 이벤트를 발생시키는 시점 (콜백 순서, 참조하는 데이터)

  - 구현 간 디버깅을 위해 각 콜백은 jsonify된 msg 속성을 포함

  - Return으로 결과를 받아와야 하는 경우, result 속성 포함

4. (필요하다면) 데이터 수집 또는 DB 연동: 활용할 수 있는 데이터 소스 준비하기

C > R > U > D ; meta tag(open graph) og:image / title / description

 

  • 클라이언트 서버 연결 확인하기

  1. Front: 진입점 확인 (상태 변화 유발 이벤트; 함수 등)

 2. Request data form 만들기

  3. Fetch로 백엔드에 전달 (전달 경로, method, data )

4. Back: 라우터에 의해 실행될 콜백 함수 정의

  5. front에서 전달한 dataForm을 기반으로 정보 처리

  6. json 형식으로 return

— 7. Front: break 또는 1로 순환

 

5. 기능 구현: 불확실한 부분(조각 기능) 먼저 시도하기

  - 구현이 미숙한 부분부터 시도하여 변수 줄이기

  - 통합 어플리케이션 작성 이전에 작은 모듈 단위로 검증 완료하기

6. 테스트 주도 개발 TDD

  - 최대한 실행 가능한 상태로 유지하며 어플리케이션 개발

  - 다양한 상수와 주석을 활용

  - 정적인 상태로 동작하도록 구현

  - 이후에 서로 다른 어플리케이션 간의 연결 (상수를 변수로 대체)

- Codecoverage: Unit jacoco Gradle ,Sonarqube using github

 

Step 5: Code Snippets

더보기

* Formdata *

 

- JQuery 대신 
formData를 활용하여 폼에 포함된 모든 데이터를 손쉽게 submit 할 수 있음

https://ko.javascript.info/formdata

 

<form id="formElem">

 <input type="text" name="name" value="Bora">

 <input type="text" name="surname" value="Lee">

 <input type="submit">

</form>

 

———————————————————————————————————————

 

* Initialize Flask Project *

1. File ‘app.py’ (entry point when using Flask with default settings)

2. Python -m venv (venv_name)

3. Folder ‘templates’

4. Pip install (libraryA) (libraryB) (…)

 

* Library 연동을 위한 삭제 및 설치 *

1. Pip freeze > requirements.txt

2. Pip uninstall -r <requirements.txt> -y : 기재된 라이브러리 모두 삭제 요청

3. Pip install -r <requirements.txt> : 기재된 라이브러리 모두 설치

pip install bs4 black flask pymongo python-dotenv requests

————————————————————————————————————

 

# init bs4

import requests

from bs4 import BeautifulSoup

def get_soup_by_url_from_bs4(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')

 return soup

soup = get_soup_by_url_from_bs4('https://bit.ly/3ntpqKH')

 

—————————————————

 

trs = list(soup.select('#body-content > div.newest-list > div > table > tbody > tr'))

for tr in trs:

 title = tr.select_one('a.title.ellipsis').text.strip()

 artist = tr.select_one('a.artist.ellipsis').text.strip()

 rank = tr.select_one('td.number').text.split()[0]

 print(rank, title, artist)

 

————————————————————————————————————

 

Pymongo

# init pymongo

import os

from dotenv import load_dotenv

from pymongo import MongoClient

 

def get_DB_by_DBname_from_pymongo(DBname):

  load_dotenv()

  root_id = os.environ.get('root_id')

  root_pw = os.environ.get('root_pw')

  client = MongoClient(

    f"mongodb+srv://{root_id}:{root_pw}@my-mongo.fnaorrz.mongodb.net/?retryWrites=true&w=majority")

  return client[f"{DBname}"]

 

db = get_DB_by_DBname_from_pymongo('test')

 

————————————————————————————————————

 

JQuery

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>

const target = $('#names-q' + 1)

 

————————————————————————————————————

 

- fetch를 비동기 방식으로 동작하도록 async-await 구문을 구사하거나,
하던대로 .then을 사용할 수 있다.

Fetch

fetch(URL)

    .then(res => res.json())

    .then(data => {

     const target = $('#names-q' + 1)

     target.empty()

     data.RealtimeCityAir.row.forEach(row => {

      const tag = `<li>${row.MSRSTE_NM} : ${row.IDEX_MVL}</li>`

      target.append(tag)

     });

    })

Step 6: AWS EC2 Beanstalk 배포 실패 (망할 Windows)

더보기

Windows 환경에서 Git bash 내 명령만으로 AWS Beanstalk 배포하려다가 실패했다.

타협해서 AWS Beanstalk만 이용해보려고 처절하게 시도한 과정이다.

 

-

 

2번 링크 진입 Deploying a Flask application to Elastic Beanstalk - AWS Elastic Beanstalk (amazon.com)

 

-

 

-

 

리눅스 호환성을 위한 하위 시스템 설치 Install WSL | Microsoft Learn

 

-

 

-

 

재부팅

 

-

Visual Studio Code > 작업 폴더 열기

터미널 > Ubuntu (WSL)

~/eb-flask$ virtualenv virt

** 만약 E: Unable to locate package python3-pip 오류가 발생한다면

  1. sudo add-apt-repository universe
  2. sudo apt-get update
  3. sudo apt-get install python3-pip
  4. virtualenv virt

 

~/eb-flask$ source virt/bin/activate

(virt)~/eb-flask$ pip install flask==2.0.3

(virt)~/eb-flask$ pip freeze > requirements.txt

 

eb-flask/application.py 생성 후 내용 작성

from flask import Flask

# print a nice greeting.
def say_hello(username = "World"):
  return '<p>Hello %s!</p>\n' % username

# some bits of text for the page.
header_text = '''
  <html>\n<head> <title>EB Flask Test</title> </head>\n<body>'''
instructions = '''
  <p><em>Hint</em>: This is a RESTful web service! Append a username
  to the URL (for example: <code>/Thelonious</code>) to say hello to
  someone specific.</p>\n'''
home_link = '<p><a href="/">Back</a></p>\n'
footer_text = '</body>\n</html>'

# EB looks for an 'application' callable by default.
application = Flask(__name__)

# add a rule for the index page.
application.add_url_rule('/', 'index', (lambda: header_text +
  say_hello() + instructions + footer_text))

# add a rule when the page is accessed with a name appended to the site
# URL.
application.add_url_rule('/<username>', 'hello', (lambda username:
  header_text + say_hello(username) + home_link + footer_text))

# run the app.
if __name__ == "__main__":
  # Setting debug to True enables debug output. This line should be
  # removed before deploying a production app.
  application.debug = True
  application.run()

-

 

(virt) ~/eb-flask$ python application.py

브라우저에서 http://localhost:5000/으로 접속 => 잘 실행됨

파일 구조는 다음과 같아야 한다.

 

-

~/eb-flask/
|-- virt
|-- application.py
`-- requirements.txt

/eb-flask/.ebignore 파일 작성

virt

-

 

EB CLI repository를 초기화하기 위해

우선 EB CLI를 설치해야 한다.

(https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install.html#eb-cli3-install.scripts) -> https://github.com/aws/aws-elastic-beanstalk-cli-setup

  • Git, Python, virtualenv가 필요하다.
  • Python은 pyenv 라이브러리를 활용하여 버전 관리하기를 추천한다고 한다.
  • pyenv를 다운로드 받으려면 Windows는 안 되고, 설정한 Ubuntu (WSL) 터미널에 homebrew가 필요하다.

-

 

Homebrew 설치

Ubuntu (WSL) 터미널로 들어가서 아래 명령어 실행

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • Warning: /home/linuxbrew/.linuxbrew/bin is not in your PATH.
  • 경고가 뜨는데 친절하게 다음 명령어를 가르쳐준다. 실행하기.
sudo apt-get install build-essential
brew install gcc

-

 

이제 pyenv 다운로드로 복귀한다.

Ubuntu 환경에서의 pyenv 다운로드 (https://wikidocs.net/10936)

 

-

sudo apt-get install curl
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash

 

-

 

이제 다시 EB CLI 설치로 돌아가자.

근데 가상 환경에서는 명령을 실행할 수 없다.

따라서 Windows '명령 프롬프트' 또는 'Powershell' 실행 이후

pip install virtualenv

만약 안 된다면 Python 환경 변수 설정을 해주어야 한다. 성공하면 아래 명령을 실행한다.

git clone https://github.com/aws/aws-elastic-beanstalk-cli-setup.git
python .\aws-elastic-beanstalk-cli-setup\scripts\ebcli_installer.py

 

설치가 완료되면, 자신의 명령 실행 환경에 따라 안내에 따라 'eb'를 환경변수로 추가한다.

1. CMD Prompt:
        cmd.exe /c "C:\Users\sean0\.ebcli-virtual-env\executables\path_exporter.bat"

2. PowerShell:
        & "C:\Users\sean0\.ebcli-virtual-env\executables\path_exporter.vbs"

Additionally, you would need to **restart this shell**

쉘은 재시작해야 한다.

-

이제 EB CLI repository를 초기화해야 한다.

 

eb init -p python-3.7 flask-tutorial --region us-east-2

(필요하다면 python 버전, 이름, 지역을 수정할 수 있다. )

이후 eb init을 다시 명령하면 설정을 진행할 수 있다. (선택적으로)

-

... 뭔가 잘못됐다. eb create에 대한 권한이 없다고 한다. 그리고 설치 환경이 자꾸 변경되는 것도 이상하다.

서버 내부 로그를 살펴보니 그냥 pywin32==(version)을 설치를 못하는거다.

OS에 따른 호환성 문제

Step 7: AWS EC2 배포 성공

더보기

Filezilla 활용해서 고정 아이피로 80 포트까지 개방하여 배포했다.

나중에 URL로 접속할 수 있도록 검색해서 시도해봐야겠다.

728x90