#1주 1일차
조원들과 각자 와이어프레임을 제출했고, 그중 가장 좋은 아이디어라고 생각된 밀크티 맛집 전문 페이지를 선택했다. (선정되고 나서 크롤링이나 정보 편리성을 위해서 그냥 카페로 확대하는 것이 어떠냐는 아이디어를 냈는데, 피드백을 듣고나선 그렇게 했으면 더 문제였을 수도 있겠다는 생각이 들었다.)
#피드백
와이어 프레임과 api를 기반으로 피드백을 받았다.
문제1.
구현하려는 기능들이 너무 많고, 특히 지도 api를 사용해서 검색 기능을 사용하려는 것은 1주일만에 진행하기 어려울 수 있다는 피드백을 받았다.
문제2.
조원들과 기능별로 역할분배를 했는데 큰틀을 잡아놓고 거기서 단계별로 나눠야하지 구현하려는 기능별로 나누는 것은 시간을 더 오래 걸리게 한다는 피드백을 받았다. (이후 프로젝트를 진행하면서 왜 그것이 문제인지 알게되었다.)
+지도 기능을 비워두고, 이를 염두해두고 시간여유가 되면 지도를 넣고, 안된다면 좀 더 구현하기 쉬운 용도로 지도공간을 사용하라는 피드백.
+깃 사용법을 익혀두면 협업에 편리함.
#1주 2일차
로그인페이지를 마무리했다. (라고 생각했다. 3일차에 취합과정에서 오류 발생)
원래 데이터베이스에 직접 리뷰 정보를 추가하려던 방향에서, 조원 한명의 이탈로 프로젝트 축소를 결정.
기존의 웹에서 크롤링해서 db에 저장 후 출력하는 방식으로 변경
#크롤링할 사이트를 선별하자(네이버 지도 / 카카오 지도 / 망고 플레이트 비교)
네이버지도나 카카오맵을 활용한다면 내 위치기반의 지역에 연관 단어 관련으로 '전국의 밀크티 맛집'이라는 구체화된 주제에 적합하지 않은 웹사이트라고 판단.
반면 망고플레이트는 우리가 원하는 주제에 맞게, 구체화되고 전국적인 정보를 제공한다고 판단해서 크롤링 대상으로 선정.
#1주 3일차
#3일차 오전부터 오류가 2가지 발생.
1. token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
pyjwt 기존 버전에서는 jwt.encode()함수의 리턴값이 '바이트 문자열'이라는 자료형이었기 때문에 뒤에 .decode('utf-8')를 붙여 일반 문자열로 바꾸어 주었음. 2.0.0 버전부터는 이 함수의 리턴값이 일반 문자열이기 때문에 여기에 또 .decode('utf-8')를 붙여주게 되면 문자열에는 이런 메소드가 없으므로 에러가 난다.
(https://namu.wiki/w/UTF-8 이걸 한번 읽어보면 좋을거 같은데... 아직까지 이해가 잘 안된다.)
2. 잘 작동하던 몽고db 가 계속해서 연결끊김 현상
pymongo.errors.serverselectiontimeouterror 리뉴얼 전 강의에서는 몽고 db를 연결할때 aws ip와 아이디 비번이 있는 연결 방식을 사용했는데, 리뉴얼된 강의대로 이렇게 연결하자. 그럼 오류 없음!
client = MongoClient('mongodb+srv://아이디:비번@cluster0.tuvzlln.mongodb.net/Cluster0?retryWrites=true&w=majority')
#망고플레이트 크롤링 코드 수정
<받은 파일>
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('http://www.mangoplate.com/search/%EB%B0%80%ED%81%AC%ED%8B%B0', headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
# 코딩 시작
cafes = soup.select('body > main > article > div.column-wrapper > div > div > section > div.search-list-restaurants-inner-wrap > ul > li')
for cafe in cafes:
a = cafe.select_one('figure > figcaption > div > a > h2')
if a is not None:
name = a.text..replace(" ", "")
# 상호명 뒤에 ()가 붙으면 긴 공백이 발생, 해당 공백을 없애주는 작업 필요 -> ok
# 근데 상호명과 ()가 다른 줄로 출력이 됨, 어떻게 해결?
star = cafe.select_one('div:nth-child(1) > figure > figcaption > div > strong').text
address = cafe.select_one('div:nth-child(1) > figure > a > div > img')['alt'].split("-")[1]
# - 까지 지워주는 걸 지정해야 함! -> 주소부분에도 -가 있어 3개로 나뉘면서 상세주소가 표기되지 않음
# [1]과 [2]를 이어줄 수 있는 방법이 있을까?
print(name, star, address)
<출력 결과>
문제1.
주소에 ()로 해서 빌딩주소까지 부가적으로 적혀있는 항목들은 공백처럼 나타난다
-> 빌딩이름이 따로 기록되어 있는 항목에는 가게이름과 빌딩이름 사이에 공백이 존재하는 듯해서 이 공백을 지워주기 위해 고민했다.
replace 처리 이후 공백을 기준으로 조인해주는 것이 필요할 것 같아서 split 통해서 공백을 자르고 join으로 연결하기로 했다.
*replace
공백없는문자열 = 공백있는문자열.replace(" ", "")
이렇게 첫 번째 매개변수에 " " 공백을 넣고,두 번째 매개변수에 "" 빈 문자열을 넣으면
'공백을 -> 빈 문자열로 변경하겠다'라는 명령이 되기 때문에
공백 있는 문자열의 공백이 사라지게 됩니다.
문제2.
망고플레이트에서 주소를 이미지와 함께 기록된 주소에서 추출을 해오려고 하는데 코드를 보면 -를 기준으로 사진과 주소를 구분할 수 있다. 대신 주소 뒷부분의 - 도 동일한 문자여서 (ex. 589-20) split을 통해 "-"기준으로 구분하면 주소가 잘리는 현상이 발생한다.
<img class="center-croping lazy" alt="뚝방길 홍차가게 사진 - 서울시 광진구 자양동 589-20" data-original="https://mp-seoul-image-production-s3.mangoplate.com/619788_1607761914172812.jpg?fit=around|359:240&crop=359:240;*,*&output-format=jpg&output-quality=80" data-error="https://mp-seoul-image-production-s3.mangoplate.com/web/resources/kssf5eveeva_xlmy.jpg?fit=around|*:*&crop=*:*;*,*&output-format=jpg&output-quality=80" src="https://mp-seoul-image-production-s3.mangoplate.com/619788_1607761914172812.jpg?fit=around|359:240&crop=359:240;*,*&output-format=jpg&output-quality=80" style="display: block;">
-> python split multiple Separator 등의 키워드로 구글링을 해봤다.
import re
text = "python is, an easy;language; to, learn."
print(re.split('; |, ', text))
여러 구분자를 동시에 사용할 때 유용할 듯 했다. 그러나 내가 원하던 해결책은 되지 않았다.
계속해서 구글링을 하다가, 앞에서부터 한번만 자르면 되지 않을까하는 생각이 들었고, maxsplit= 이라는 방법을 발견했다.
<수정 완료 후 파일>
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('http://www.mangoplate.com/search/%EB%B0%80%ED%81%AC%ED%8B%B0', headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
# 코딩 시작
cafes = soup.select('body > main > article > div.column-wrapper > div > div > section > div.search-list-restaurants-inner-wrap > ul > li')
for cafe in cafes:
a = cafe.select_one('figure > figcaption > div > a > h2')
if a is not None:
name = " ".join(a.text.split())
star = cafe.select_one('div:nth-child(1) > figure > figcaption > div > strong').text
address = cafe.select_one('div:nth-child(1) > figure > a > div > img')['alt'].split("-",maxsplit=1)[1]
print(name, star, address)
<출력 결과>
#피드백(오류 발생)
((지도 api가 다루기가 복잡하다면 다른 기능들을 구현하고, 지도 api 를 포기하는 것도 방법이라는 조언을 들었다. 동시에 해주신 말씀은 가능할 것 같고, 해보고 싶다면 일단 내일 제출전까지 마무리할 수 있도록 최대한 노력해보고 이 과정에서 실패하더라도 얻는게 있을 것이라는 것이였다. 조원이 나가는 경험도 있었고, 조원들 모두 포기하지 않고 할 수 있다고 생각했기에 지도 구현을 하기로 했다.))
- 크롤링한 사이트에서 가져온 주소정보를 기반으로 x, y 좌표를 찍어 데이터베이스에 저장하고 이를 기반으로 좌표를 찍으려고 했다.
- db.py 에서 보면 이름과 별점, 주소를 가져오고 이를 데이터베이스에 저장해두는 코드는 있었으나, 이를 x,y 좌표로 변환하는 부분이 누락되어 있었다. (이외에 그 좌표를 찍어주는 기능들은 정상적으로 구성되있었으나, 좌표변환 과정이 누락되어 모든게 작동하지 않던 상황)
- +데이터베이스에 이미 저장을 해두고, 그 저장된 파일을 불러오기만 하면 되는건데 서버 자체에 db에 저장하는 코드를 작성해 놓다보니 서버에서 새로고침 할때마다 db에 있는 자료들을 그대로 계속 카드란에 붙이는 듯 했다.
- 서버에 있는 db 저장 코드를 삭제하고, 따로 작성된 db.py 에 x,y 좌표 변환기능을 추가한 코드 구성
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient
client = MongoClient('몽고 주소 넣기')
db = client.milmmelier
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('http://www.mangoplate.com/search/%EB%B0%80%ED%81%AC%ED%8B%B0', headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
cafes = soup.select('body > main > article > div.column-wrapper > div > div > section > div.search-list-restaurants-inner-wrap > ul > li')
for cafe in cafes:
a = cafe.select_one('figure > figcaption > div > a > h2')
if a is not None:
title = " ".join(a.text.split())
star = cafe.select_one('div:nth-child(1) > figure > figcaption > div > strong').text
address = cafe.select_one('div:nth-child(1) > figure > a > div > img')['alt'].split("-",maxsplit=1)[1]
headers = {
"X-NCP-APIGW-API-KEY-ID": "m1qc7sxv4t",
"X-NCP-APIGW-API-KEY": "utc5druQG9LHR1sV4W18XLDVS562LrTLqVTFLCS4"
}
r = requests.get(f"https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query={address}",
headers=headers)
response = r.json()
if response["status"] == "OK":
if len(response["addresses"]) > 0:
x = float(response["addresses"][0]["x"])
y = float(response["addresses"][0]["y"])
print(title, address, star, x, y)
doc = {
'star': star,
'title': title,
'address': address,
"mapx": x,
"mapy": y}
db.matjips.insert_one(doc)
undefined 은 해결!
+추가 문제 발생
- 처음 화면을 띄웠을때, 카드박스가 뜨지 않고 웹사이트 내의 새로고침 버튼을 눌러야 발생.
- 멘토님께 질문해서 같이 고민한 결과 기존 function get_matjips() 함수 내의 첫번째 empty() 함수 자체를 삭제하고, (doucument).ready 안에 get_matjips() 를 넣음. 이후 버튼에 기존 할당 되있던 get_matjips()를 제거하고 window.location.reload() 를 할당해서 사이트가 새로고침 되게 만들면서 해결. (끝까지 고민하고 같이 해결해주신 멘토님께 감사했다..)
(document).ready(function ()
function get_matjips() {
for (let i = 0; i < markers.length; i++) {
markers[i].setMap(null);
infowindows[i].clone()
}
markers = [];
infowindows = [];
$.ajax({
type: "GET",
url: '/matjip',
data: {},
success: function (response) {
let matjips = response["matjip_list"]
for (let i = 0; i < matjips.length; i++) {
let matjip = matjips[i];
make_card(i, matjip);
let marker = make_marker(matjip);
add_info(i, marker, matjip)
}
}
});
}
정상적으로 카드와 마크 그리고 정보창이 뜨는걸 확인후 마무리!
#1주 4일차
#제출 화면 https://www.youtube.com/watch?v=5FVSqzKxk8o
#피드백
클린 코드, 함께 자라기 책 두개 추천
#이번주차 핵심과 마무리
*jwt
WT 방식이란 로그인 등의 인증에 필요한 정보를 JSON Web Token으로 암호화 시키는 방식. Header, Payload, Signature로 구성
Header는 암호화할 해싱 알고리즘 및 토큰의 타입을 지정하는 부분. Payload는 ID, 유효기간과 같이 토큰에 담겨 서버에 보내질 데이터를 담는 부분. Signature는 Header, Payload를 더한 뒤 SECRET KEY를 해싱하여 생성하는 부분입니다. 서버 측의 SECRET KEY가 유출되지 않는 한, 토큰의 위조와 변조가 불가능하기 때문에 토큰의 위조와 변조 여부 확인에도 용이하다.
jwt방식의 구동순서
클라이언트에서 로그인 요청이 들어오면 ①서버에서 검증 ②ID 등의 고유 정보를 Payload에 담기 ③SECRET KEY를 이용해 Access Token인 JWT 발급 ④토큰을 클라이언트에 저장(서버에 요청이 있을 때마다 전달) ⑤서버에서 토큰의 Signature를 SECET KEY로 복호화 ⑥위·변조 여부 및 유효기간 확인 ⑦유효한 토큰일 경우 요청에 응답 ⑧로그인의 결과를 만들어냅니다.
JWT가 쿠키/세션에 비해 가지는 가장 큰 장점은 인증정보에 대한 별도의 저장소가 필요없으며 서버에 인증정보가 저장되지 않아 서버의 확장, 유지 및 보수가 용이하다는 것입니다.
*api
라이브러리에 접근하기 위한 규칙들을 정의한 것을 API라고 하는데 Application Program Interface 즉, 프로그래머가 라이브러리가 제공하는 여러 함수를 이용하여 프로그램을 작성할 때 해당 함수의 내부 구조는 알 필요없이 단순히 API에 정의된 입력값을 주고 결과 값을 사용할 수 있게 해줍니다.
api를 사용함으로써 개발자들은 쉽게 기존의 방식을 활용한 프로그램 개발이 가능하고, 사용자들은 이미 친숙한 api를 통해 새로운 프로그램을 사용함으로써 기술 접근도를 높일 수 있다는 장점이 있다.
'개발 > 항해99' 카테고리의 다른 글
항해99-6주차 기록(미니프로젝트) (0) | 2022.07.31 |
---|---|
항해99-5주차 기록(주특기 숙련 -React) (0) | 2022.07.24 |
항해99-4주차 기록(주특기 심화-React) (0) | 2022.07.17 |
항해99-3주차 기록(주특기 입문-React) (0) | 2022.07.07 |
항해99-2주차 기록(알고리즘) (0) | 2022.06.26 |