스파르타 코딩클럽 내일배움캠프 AI 웹개발자양성과정 3회차
2022.08.30. 2일차 - TIL
1. 미니프로젝트
오늘은 각자 만든 페이지를 종합했다. 단순히 한 메인페이지에 각자의 개인페이지를 연결시키는 작업이라 수월하게 생각했는데 생각보다 이것저것 손봐야 할 부분이 있어서 1시간 정도의 시간이 걸렸다.(코드 정독하느라고 눈이 너무 아팠다...) 튜터님의 난이도 조절로 극강으로 안심해버린 우린 팀은 정말 기본적인 프론트엔드 부분만 만지게 되었고, 코딩 부분을 설명할 것이 하나도 없음에 급하게 각자 페이지를 조금씩 더 발전시키는 방향으로 프로젝트를 진행했다.(매니저님의 피드백도 크게 도움이 되었다) 그래서 나는 개인시간에 각자의 flask 서버에 페이지를 연동하고, 댓글 기능을 추가하고, 이미지를 구글 드라이브를 통해 공유했다. 뒤에 내용을 정리하겠지만 생각보다 오늘 알게 된 것 중 저 이미지를 클라우드 서비스에 올리는 개념이 내게 큰 도움이 되었다. 이전까지 mongoDB에 이미지를 저장할 때 url을 저장해 그럼 이미지 자체는 어떻게 관리하는 지에 대한 의문이 들었는데 저렇게 클라우드 서비스(결국 구글 드라이브도 하나의 큰 DB인 것이다)를 통해 이미지를 관리하고 그 url만 mongoDB에 넣으면 이미지 처리가 수월할 것이라는 생각이 들었다. 이미지를 위한 전용 서버가 필요하다는 말은 어쩌면 저런 말일지도 모르겠다.
2. 웹 프로그래밍 A-Z 기초 강의
- JSON은 자료형 딕셔너리 구조와 유사하다 -> KEY-VALUE의 구조
- 클라이언트 -> 서버 요청(다양한 타입 존재)
- GET 방식 : 데이터 조회
- POST 방식 : 데이터 생성, 변경, 삭제 - GET 방식으로 데이터 전달
- ? : 이후 부분은 전달할 데이터 작성
- & : 전달할 데이터가 더 존재 - POST 방식은 data : {} 에 넣어 데이터 전달
- Ajax는 jQuery를 임포트한 페이지에서만 동작
- ajax 기본 골격
$.ajax({
type: "GET", // GET 방식으로 요청한다.
url: "http://spartacodingclub.shop/sparta_api/seoulair", //클라이언트가 통신을 요청하는 서버
data: {}, // 요청하면서 함께 줄 데이터 (GET 요청시엔 비워두세요)
success: function(response){ // 서버에서 준 결과를 response라는 변수에 담음
console.log(response) // 서버에서 준 결과를 이용해서 나머지 코드를 작성
}
})
- 이미지 변경, 텍스트 변경 jQuery
//이미지 바꾸기
$("#아이디값").attr("src", 이미지URL);
//텍스트 바꾸기
$("#아이디값").text("바꾸고 싶은 텍스트");
- 로딩 후 호출
$(document).ready(function(){
alert('다 로딩됐다!')
});
- 가상환경 : 프로젝트 별 패키지들을 담은 공구함 -> 프로젝트의 venv 폴더
- 크롤링 기본 세팅
import requests
from bs4 import BeautifulSoup
# 타겟 URL을 읽어서 HTML를 받아오고,
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)
# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
# soup이라는 변수에 "파싱 용이해진 html"이 담긴 상태가 됨
# 이제 코딩을 통해 필요한 부분을 추출하면 된다.
soup = BeautifulSoup(data.text, 'html.parser')
#############################
# (입맛에 맞게 코딩)
#############################
- 태그 안의 텍스트를 찍고 싶을 때 -> 태그.text
- 태그 안의 속성을 찍고 싶을 때 -> 태그['속성']
- pymongo 기본 세팅
from pymongo import MongoClient
client = MongoClient('mongodb+srv://test:sparta@cluster0.55vah.mongodb.net/Cluster0?retryWrites=true&w=majority')
db = client.dbsparta
- pymongo 코드
# 저장 - 예시
# 보통 doc으로 만들어 insert
doc = {'name':'bobby','age':21}
db.users.insert_one(doc)
# 한 개 찾기 - 예시
user = db.users.find_one({'name':'bobby'})
# 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
all_users = list(db.users.find({},{'_id':False}))
# 바꾸기 - 예시
db.users.update_one({'name':'bobby'},{'$set':{'age':19}})
# 지우기 - 예시
db.users.delete_one({'name':'bobby'})
- python의 strip() 함수 : 문자열의 시작과 끝에서 공백(특정 문자도 가능)을 제거하고, 공백없이 동일한 문자열 반환
3. 코드(내가 한 부분만...)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
from pymongo import MongoClient
client = MongoClient('mongodb+srv://아이디:비밀번호@cluster0.2i2nlsz.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta
@app.route('/')
def home():
return render_template('index.html')
@app.route('/seongyeon_info')
def sy_page():
return render_template('seongyeon_info.html')
@app.route('/intro_card')
def th_page():
return render_template('intro_card.html')
@app.route('/mypage')
def cj_page():
return render_template('mypage.html')
@app.route('/yjint')
def yj_page():
return render_template('yjint.html')
@app.route('/slide')
def slide_page():
return render_template('slide.html')
@app.route("/seongyeon_info/comment", methods=["POST"])
def comment_post_sy():
name_receive = request.form["name_give"]
comment_receive = request.form["comment_give"]
doc = {
'name': name_receive,
'comment': comment_receive
}
db.sy_comment.insert_one(doc)
return jsonify({'msg': '댓글 완료!'})
@app.route("/seongyeon_info/comment", methods=["GET"])
def comment_get_sy():
comment_list = list(db.sy_comment.find({},{'_id':False}))
return jsonify({'comments':comment_list})
@app.route("/intro_card/comment", methods=["POST"])
def comment_post_th():
name_receive = request.form["name_give"]
comment_receive = request.form["comment_give"]
doc = {
'name': name_receive,
'comment': comment_receive
}
db.th_comment.insert_one(doc)
return jsonify({'msg': '댓글 완료!'})
@app.route("/intro_card/comment", methods=["GET"])
def comment_get_th():
comment_list = list(db.th_comment.find({},{'_id':False}))
return jsonify({'comments':comment_list})
@app.route("/mypage/comment", methods=["POST"])
def comment_post_cj():
name_receive = request.form["name_give"]
comment_receive = request.form["comment_give"]
doc = {
'name': name_receive,
'comment': comment_receive
}
db.cj_comment.insert_one(doc)
return jsonify({'msg': '댓글 완료!'})
@app.route("/mypage/comment", methods=["GET"])
def comment_get_cj():
comment_list = list(db.cj_comment.find({},{'_id':False}))
return jsonify({'comments':comment_list})
if __name__ == '__main__':
app.run('0.0.0.0',port=5000,debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<title>Information</title>
<link href="https://fonts.googleapis.com/css2?family=Jua&display=swap" rel="stylesheet">
<style>
* {
font-family: 'Jua', sans-serif;
}
.container {
margin: 30px 15px 30px 15px;
width: 95%;
max-width: 1200px;
}
.post_title {
margin-top: 50px;
padding: 10px;
font-size: 25px;
}
.post_name {
margin-top: 10px;
padding: 10px;
font-size: 25px;
}
.post_content {
margin-top: 10px;
padding: 10px;
font-size: 25px;
}
.image {
margin-top: 15px;
display: flex;
flex-direction: row;
}
.image>img {
margin-right: 10px;
}
.btn-light {
margin-top: 15px;
width: 80px;
font-size: 25px;
background-color: lightgray;
color: black;
}
.btn-light:hover {
margin-top: 15px;
width: 80px;
font-size: 25px;
background-color: black;
color: lightgray;
}
.comments {
border-width: 1px;
border-color: lightgray;
border-style: solid;
border-radius: 5px;
margin-top: 15px;
padding: 10px;
}
.comment_btn {
margin-top: 15px;
}
hr {
border: 0px;
border-top: 5px dotted lightgray;
}
.comment_list > .card {
margin-top: 10px;
margin-bottom: 10px;
}
</style>
<script>
$(document).ready(function () {
show_comment();
});
function back() {
location.href = "/"
}
function show_comment() {
$('#comment_list').empty()
$.ajax({
type: "GET",
url: "/seongyeon_info/comment",
data: {},
success: function (response) {
let rows = response['comments']
for (let i = 0; i < rows.length; i++) {
let name = rows[i]['name']
let comment = rows[i]['comment']
let temp_html = `<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>${comment}</p>
<footer class="blockquote-footer">${name}</footer>
</blockquote>
</div>
</div>`
$('#comment_list').append(temp_html)
}
}
});
}
function save_comment() {
let name = $('#name').val()
let comment = $('#comment').val()
$.ajax({
type: "POST",
url: '/seongyeon_info/comment',
data: { 'name_give': name, 'comment_give': comment },
success: function (response) {
alert(response["msg"])
window.location.reload()
}
});
}
</script>
</head>
<body>
<div class="container">
<div class="post_title">
<div class="form-group">
<label for="exampleFormControlTextarea1">Title</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="1" style="font-size: 20pt" readonly>좋은 개발자가 되고 싶습니다</textarea>
</div>
</div>
<div class="post_name">
<div class="form-group">
<label for="exampleFormControlTextarea1">Name</label>
<textarea class="form-control" id="exampleFormControlTextarea2" rows="1" style="font-size: 20pt" readonly>이승연</textarea>
</div>
</div>
<div class="post_content">
<div class="form-group">
<label for="exampleFormControlTextarea1">Content</label>
<textarea class="form-control" id="exampleFormControlTextarea3" rows="12" style="font-size: 20pt" readonly>안녕하세요! 만나서 반갑습니다:) 저는 이승연입니다. 제 mbti는 infp입니다. 매우 내향적이고 낯을 많이 가리니 처음에 말이 별로 없어도 계속 말 시켜주세요ㅎㅎ 저의 장점은 평화를 사랑한다는 것입니다. 타협과 절충 아주 좋아합니다. 또 협동에 있어서 팔로우형입니다. 몇번 리드형을 맡은 적이 있는데 정말 할 때마다 적성에 맞지 않다는 것을 하루하루 느꼈습니다. tmi는 고양이 3마리를 기르고 있는데 동생이 길에서 냥줍하고 그 동생은 현재 군대에 간 상태라는 것입니다. 좋아하는 것은 커피, 홍차, 마라 제외 향신료, youtube에서 플레이리스트 들어 놓고 작업하기이고, 싫어하는 것은 참새보다 큰 새, 마라, 비린 음식, 살코기 외 다른 부위(껍데기, 족발, 내장...) 입니다. 아직 부족한 것도 많고, 조바심에 하루하루가 불안할 때가 많지만 이번 캠프를 통해 좋은 결과가 있을 것이라 기대하면 더욱 노력하겠습니다!
</textarea>
<div class="image">
<img src="http://drive.google.com/uc?export=view&id=1dm01UhkD3U4CM6VvVUq7QFXMLmsqPaV2" width="350" height="450">
<img src="http://drive.google.com/uc?export=view&id=104UGJuxITgHcbkPno9hxo8Q36jIXphx5" width="350" height="450">
<img src="http://drive.google.com/uc?export=view&id=1wJvEMfKVX4qYyfzzNMmNOBRrTlrElBqv" width="350" height="450">
</div>
<div class="comments">
<div class="comment_input">
<form>
<div class="form-group">
<label for="name">Name</label>
<textarea class="form-control" id="name" rows="1"></textarea>
</div>
<div class="form-group">
<label for="comment">Comment</label>
<textarea class="form-control" id="comment" rows="3"></textarea>
</div>
</form>
<div class="comment_btn">
<button onclick="save_comment()" type="button" class="btn btn-outline-dark">Save</button>
</div>
</div>
<hr>
<div class="comment_list" id="comment_list">
<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>안녕하세요!</p>
<footer class="blockquote-footer">호빵맨</footer>
</blockquote>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-light" onclick="back()">목록</button>
</div>
</div>
</div>
</body>
</html>
4. 후기
오늘 느낀점은 코드는 역시 깔끔하고 유형화(?) 되게 짜는 게 최고이다. 동일한 기능이라도 다른 팀원들의 코드에 추가할 때 css가 복잡하게 되어 있으니 하나하나 찾아가서 고쳐야 하는게 정말 번거로웠다. 코드는 깔끔하게!!!! 정형화해서!!!! 다른 사람이 알아보기 쉽게!!! 들여쓰기 확실하게!!!! 이런 느낌...? 인강을 반복해서 듣는 것이 귀찮기도 하지만 계속 복습해서 힘들이지 않고 코드에 익숙해지고, 또 강의가 지루하다 싶으면 프로젝트로 넘어와 열심히 코딩하다가 지치면 돌아와 강의를 듣는 방법이 은근 효과적이다. 오늘 하루도 알차게 보냈다. 개발 잘 하고 싶다~~~
'개발일지 > AI 캠프' 카테고리의 다른 글
내일배움캠프 AI - 4일차 TIL, 2022.09.01 (0) | 2022.09.02 |
---|---|
A2조 - KPT 회고 (0) | 2022.09.01 |
내일배움캠프 AI - 3일차 TIL, 2022.08.31 (0) | 2022.09.01 |
내일배움캠프 AI - 1일차 TIL, 2022.08.29 (1) | 2022.08.30 |
내일배움캠프 A-2조(내향인캠프조) 미니프로젝트 S.A (0) | 2022.08.26 |