스파르타 코딩클럽_내일 배움 캠프 spring 트랙 6기
[프로젝트] 팀 소개페이지 미니 프로젝트
2023.05.15 ~ 2023.05.19
<프로젝트 정보>
개발도구 : Visual Studio Code
프로그래밍 언어 : Python
데이터베이스 : MongoDB
프레임워크 : Flask
라이브러리 : venv
와이어프레임 : Figma
<프로젝트 내용>
<div class="member">
<div class="row row-cols-1 row-cols-md-5 g-2">
<div class="col">
<div class="card h-100">
<a href= {{url_for('sub')}}>
<img src="멤버사진url" class="img" alt="..."></a>
<div class="name">
<h3>멤버이름</h3>
</div>
</div>
</div>
<img src="멤버사진url" class="click_img alt=" ... onclick="profile()"> 기존 코드에서는 onclick을 사용하여 index.html 상단 부분에 Javascript를 이용하여 멤버들의 사진을 누르면 개인 소개페이지(서브페이지)로 동작할 수 있는 함수를 만들어서 적용시켰었다.
그러나 멤버 인원이 5명이어서 멤버별로 데이터를 받아와야 했으나 아직은 구현할 수 있는 기술이 아니어서 페이지를 다 따로 만들어야했기에
oncilck대신 <a href = {{url_for('sub')}}로 연결하여 각 DB에서 바로 받아올 수 있도록 코드를 바꾸었다.
여러번 수정을 거치면서 git에 push하는 방법도 첫 날보다는 많이 익숙해졌다.
<index.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Document</title>
<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://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- 구글 폰트 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Oswald:wght@500&family=Rampart+One&display=swap"
rel="stylesheet">
<title> E1I4 </title>
<style>
/* 구글 폰트 */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Oswald:wght@500&display=swap');
/* 전체 폰트 지정 */
#nav,
#bookmarkA,
#bookmarkB,
#bookmarkC,
#bookmarkD {
font-family: 'Noto Sans KR', sans-serif;
}
/* 전체 바디 사이즈 1320px */
.wrap {
/* 웹종반 1-9 강의 참고 */
display: flex;
flex-direction: column;
align-items: center;
width: 1320px;
height: 7000px;
/* 나중에 값 변경할 것 */
margin: 0px auto 0px auto;
text-align: center;
}
/* -------------------------------------- 여기까지 팀원 -------------------------------------- */
/* 메인 배너 전체 사이즈와 정렬 */
#main_banner {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 1320px;
height: 300px;
}
/* 메인 타이틀 글자 설정 */
#main_title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 120px;
/* font-weight: bold; */
letter-spacing: 4px;
font-family: 'Rampart One', cursive;
color: black;
/* text-shadow: -1px 0 #000, 0 2.5px #000, 1.5px 0 #000, 0 -1px #000; */
}
/* 네비게이션바 크기, 색*/
#nav {
width: 1320px;
height: 50px;
background-color: #FF9494;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding-top: 15px;
}
#nav>ol {
display: flex;
flex-direction: row;
margin-right: 40px;
}
#nav>ol>li {
display: flex;
flex-direction: row;
margin: 0px 53px 0px 53px;
list-style: none;
font-size: 28px;
}
#nav>ol>li>a {
color: white;
text-decoration: none;
}
#nav>ol>li>a:hover {
text-decoration: none;
color: black;
}
/* 백설이 탑으로 가는 버튼 */
#top100 {
width: 50px;
height: 50px;
border: 1px solid black;
z-index: 10;
display: flex;
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 10px;
height: 10px;
border: 1px solid black;
z-index: 10;
display: flex;
position: fixed;
bottom: 110px;
right: 200px;
border: 0px;
}
#top100>img {
width: 100px;
height: 100px;
border-radius: 15px;
}
#top100>p {
font-size: 20px;
text-decoration: none;
}
#top1 {
text-decoration: none;
color: black;
}
#top1>a:hover {
color: black;
}
/* 백설이 애니메이션 관련코드(코딩네컷 완주보상) */
@keyframes rtanee {
0% {
bottom: 0px;
}
100% {
bottom: 100px;
}
}
.white {
position: fixed;
/* right: 10px;
bottom: 10px; */
/* 아이콘 이동 애니메이션 관련 설정 */
animation-name: rtanee;
animation-duration: 2s;
animation-duration: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
}
.white {
width: 150px;
cursor: pointer;
}
/* -------------------------------------- 여기까지 팀원 -------------------------------------- */
/* -------------------------------------- 아래는 팀원 -------------------------------------- */
.team-body {
box-sizing: border-box;
width: 920px;
height: 209px;
border: 2px solid #000000;
margin: auto;
justify-content: center;
align-items: center;
font-size: 20px;
line-height: 24px;
text-align: center;
padding-top: 10px;
}
#imoji {
font-size: 40px
}
.card-title {
margin: 150px auto 30px auto;
}
.card-body {
width: 400px;
background-color: #FFF5E4;
height: 467px;
left: 760px;
top: 1086px;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 150%;
text-align: left;
}
/* -------------------------------------- 여기까지는 팀원 -------------------------------------- */
/* -------------------------------------- 아래는 팀원 -------------------------------------- */
.title {
padding: 30px;
}
.name {
margin: auto;
}
.card {
border-color: white;
}
.img {
width: 200px;
height: 200px;
border-radius: 100%;
}
/* -------------------------------------- 아래는 팀원 -------------------------------------- */
</style>
<script>
/* -------------------------------------- 아래는 팀원 : guset book --------------------------------------*/
function save_comment() {
let name = $('#name').val()
let comment = $('#comment').val()
let formData = new FormData();
formData.append("name_give", name);
formData.append("comment_give", comment);
fetch('/guestbook', { method: "POST", body: formData, }).then((res) => res.json()).then((data) => {
alert(data["msg"]);
window.location.reload()
});
}
$(document).ready(function () {
show_comment();
});
function show_comment() {
fetch('/guestbook').then((res) => res.json()).then((data) => {
let rows = data['result']
$('#comment-list').empty()
rows.forEach((a) => {
let name = a['name']
let comment = a['comment']
let temp_html = `<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<div style="width:100px; height:70px; border:1px solid grey; float:left;">
${comment}
</div>
<div style="width:150px; height:70px; border:1px solid grey; float:right;">
<footer class="blockquote-footer">${name}</footer>
</div>
</blockquote>
</div>
</div>`
$('#comment-list').append(temp_html)
})
})
}
// -------------------------------------- 여기까지 팀원 --------------------------------------
</script>
</head>
<body>
<!-- 팀원 시작-->
<div class="wrap" id="top1">
<div id="main_banner">
<div>
<div>
<h1 id="main_title">
<p>E1I4</p>
</h1>
</div>
</div>
</div>
<div id="nav">
<ol>
<li><a href="#bookmarkA">Intro</a></li>
<li><a href="#bookmarkB">E1I4</a></li>
<li><a href="#bookmarkC">멤버 소개</a></li>
<li><a href="#bookmarkD">방명록</a></li>
</ol>
</div>
<a href="#top1" id="top1">
<div id="top100">
<p>TOP</p>
<img src="\static\image\백설.png" class="white">
</div>
</a>
<!-- 팀원 끝-->
<!-- 팀원 시작-->
<div class="teamintro">
<img src="/static/image/title1_2.png" style="margin: 109px 200px auto 200px">
<div class="team-body" id="bookmarkA">
<p>안녕하세요
<br>저희는 E 1명과 I 4명으로 구성된 화목한 5남매(+1) 입니다
<p id="imoji">🙋🏻♂️🧚🏻♂️👩🏻🐶👩🏻🦰👰🏻♀️</p>
<br>모두 비전공자이고 나이도 다르지만, 프로젝트를 완성하고자
<br>의기투합하여 한 마음 한 뜻으로 똘똘 뭉쳤습니다 V
</p>
</div>
</div>
<div class="teaminfo" id="bookmarkB">
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card h-100">
<h2 class="card-title">TMI / 우리팀의 특징</h2>
<div class="card-body">
<img src="/static/image/Frame 2.jpg" class="card-img-top" alt="...">
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<h2 class="card-title">궁극적인 목표</h2>
<div class="card-body">
<br> ✔ Git 사용법 숙지하기
<br> ✔ 내일배움캠프 포기하지 않고 끝까지 수료하기!
<br> ✔ 프론트와 백의 연결관계 이해하기
<br> ✔ POST, GET 사용법 마스터하기
<br> ✔ ch1 미니 project 구조 정확히 이해하고 만들기
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<h2 class="card-title">우리팀의 규칙</h2>
<div class="card-body">
<br> ✔ 건강이 제일 중요! 밥 세끼 꼬박꼬박 잘 챙겨먹기💕
<br> ✔ 막히는 부분이 있다면 혼자 머리싸매지 말고 팀원과 같이 해결!
<br> ✔ 13-14시 점심 시간, 18-19시 저녁 시간 🐷
<br> ✔ 20시 Git Merge 후 팀원들과 함께 코드리뷰 , 상세 피드백하기
<br> ✔ 20시 40분 개발 일지(TIL) 작성하고 퇴실시간 맞춰 퇴실하기
</div>
</div>
</div>
</div>
</div>
<!-- 팀원 끝-->
<div style="height: 200px;">
</div>
<!-- 팀원 시작-->
<div class="title" id="bookmarkC">
<h2>MEMBER</h2>
</div>
<div class="member">
<div class="row row-cols-1 row-cols-md-5 g-2">
<div class="col">
<div class="card h-100">
<a href={{url_for('sub1')}}>
<img src="팀원이미지URL" class="img"
alt="..."></a>
<div class="name">
<h3>팀원</h3>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<a href={{url_for('sub1')}}>
<img src="팀원이미지URL" class="img"
alt="..."></a>
<br>
<div class="name">
<h3>팀원</h3>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<a href={{url_for('sub1')}}>
<img src="팀원이미지URL" class="img"
alt="..."></a>
<br>
<div class="name">
<h3>팀원</h3>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<a href={{url_for('sub1')}}>
<img src="팀원이미지URL" class="img"
alt="..."></a>
<br>
<div class="name">
<h3>팀원</h3>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<a href={{url_for('sub1')}}>
<img src="팀원이미지URL" class="img"
alt="..."></a>
<br>
<div class="name">
<h3>팀원</h3>
</div>
</div>
</div>
</div>
</div>
<!-- 팀원 -->
<!-- 아래는 팀원 상세페이지 이동 버튼 나중에 삭제함 -->
<div>
<button onclick="location.href='/myprofile/new'">상세페이지</button>
</div>
<!-- 팀원 시작-->
<div class="mypic" id="bookmarkD">
<h1>GUEST BOOK</h1>
</div>
<div class="mypost">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="name" placeholder="닉네임" />
</div>
<div class="form-floating">
<textarea class="form-control" placeholder="방명록을 작성하세요" id="comment"></textarea>
<!-- style="height: 100px" -->
</div>
<button onclick="save_comment()" type="button" class="btn btn-dark">
댓글 남기기
</button>
</div>
<div class="mycards" 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 class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>새로운 앨범 너무 멋져요!</p>
<footer class="blockquote-footer">호빵맨</footer>
</blockquote>
</div>
</div>
<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>새로운 앨범 너무 멋져요!</p>
<footer class="blockquote-footer">호빵맨</footer>
</blockquote>
</div>
</div>
</div>
</div>
<!-- 팀원 끝 -->
</body>
</html>
<sub.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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>
<link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />
<title>상세페이지</title>
<style>
::selection {
user-select: none
/* 드래그 못하게 */
}
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #2f3542;
border-radius: 10px;
background-clip: padding-box;
border: 2px solid transparent;
}
.container::-webkit-scrollbar-track {
background-color: grey;
border-radius: 10px;
box-shadow: inset 0px 0px 5px white;
}
.container {
position: absolute;
width: 1140px;
height: 890px;
left: 390px;
top: 29px;
}
.imoge {
position: absolute;
width: 49px;
height: 41px;
left: 523px;
top: 151px;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 70px;
line-height: 85px;
/* background-color: red; */
text-shadow: 0px 8px 20px rgba(0, 0, 0, 0.25);
}
.photo {
box-sizing: border-box;
position: absolute;
width: 293px;
height: 283px;
left: 523px;
top: 248px;
box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.25);
border-radius: 50px;
background-image: url('https://cdn.pixabay.com/photo/2023/04/03/03/20/plant-7895897_1280.jpg');
background-repeat: no-repeat;
background-position: center;
}
.name {
position: absolute;
width: 250px;
height: 77px;
left: 890px;
top: 296px;
font-family: 'Inter';
font-weight: 600;
font-size: 60px;
line-height: 77px;
}
.about_me_area {
position: absolute;
width: 288px;
height: 158px;
left: 905px;
top: 373px;
background: transparent;
}
.about_me_text {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
}
#tmi_area {
position: absolute;
width: 864px;
height: 323px;
left: 523px;
top: 566px;
background: rgba(217, 217, 217, 0.22);
box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.25);
border-radius: 80px;
}
.profile {
position: absolute;
left: 80px;
top: 40px;
font-family: 'Roboto';
font-style: normal;
font-weight: 567;
font-size: 48px;
line-height: 56px;
font-variant: small-caps;
}
.text {
background-color: transparent;
position: absolute;
left: 100px;
top: 100px;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 17px;
}
.text:hover {
outline: none;
cursor: pointer
}
summary::marker {
content: "+ ";
font-family: monospace;
font-size: 16px;
}
details[open] summary::marker {
content: "- ";
}
.button {
border: none;
position: absolute;
width: 133px;
height: 53px;
left: 1262px;
top: 911px;
background: #4A99E9;
color: white;
box-shadow: 0px 8px 10px rgba(0, 0, 0, 0.25);
border-radius: 50px;
}
.button:hover {
background: #3E81C4;
}
.button:active {
transform: scale(0.95);
}
</style>
<script>
window.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('details').forEach(function (item) {
item.addEventListener("toggle", event => {
let toggled = event.target;
if (toggled.attributes.open) {
document.querySelectorAll('details[open]').forEach(function (opened) {
if (toggled != opened)
opened.removeAttribute('open');
});
}
})
});
});
$(document).ready(function () {
show_profiles();
console.log("ready")
});
function show_profiles() {
fetch('/myprofile/new1').then(res => res.json()).then(data => {
let rows = data['result']
console.log(rows)
$('#container').empty();
rows.forEach((a) => {
let imoge = a['imoge']
let photo = a['photo']
let name = a['name']
let about_me = a['about_me']
let Q1 = a['Q1']
let Q2 = a['Q2']
let Q3 = a['Q3']
let Q4 = a['Q4']
let temp_html = `<div class="container" id="container">
<div>
<text class="imoge">${imoge}</text>
</div>
<div class="photo">${photo}</div>
<div>
<text id="name" class="name">${name}</text>
</div>
<div id="test" class="about_me_area">
<text class="about_me_text">${about_me}</text>
</div>
<div id="tmi_area">
<text class="profile">
Profile
</text>
<div class="text">
<details>
<summary>사용할 수 있는 언어</summary>
<p>${Q1}</p>
</details>
<details>
<summary>자신의 장점</summary>
<p>${Q2}</p>
</details>
<details>
<summary>나의 MBTI</summary>
<p>${Q3}</p>
</details>
<details>
<summary>TMI</summary>
<p>${Q4}</p>
</details>
</div>
</div>
<button type="button" onclick="location.href='http://localhost:5000/' " class="button">뒤로가기</button>
</div>`
$('#container').append(temp_html)
})
})
}
</script>
</head>
<body>
<div class="container" id="container">
<div>
<text class="imoge">🐶</text>
</div>
<div class="photo"></div>
<div>
<text class="name">이름</text>
</div>
<div id="test" class="about_me_area">
<text class="about_me_text">자기소개</text>
</div>
<div id="tmi_area">
<text class="profile">
Profile
</text>
<div class="text">
<details>
<summary>사용할 수 있는 언어</summary>
<p>내부에 넣을 내용을 입력해주세요</p>
</details>
<details>
<summary>자신의 장점</summary>
<p>내부에 넣을 내용을 입력해주세요</p>
</details>
<details>
<summary>나의 MBTI</summary>
<p>내부에 넣을 내용을 입력해주세요</p>
</details>
<details>
<summary>TMI</summary>
<p>내부에 넣을 내용을 입력해주세요</p>
</details>
</div>
</div>
<button type="button" onclick="location.href='http://localhost:5000/' " class="button">뒤로가기</button>
</div>
</body>
<app.py>
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
# 맥 사용자는 풀어서 사용하세요 - 본인 mongoDB로 변경하기
# import certifi
# ca = certifi.where()
# mongoDB는 본인 mongoDB로 변경하기
from pymongo import MongoClient
# 멤버1mongoDB
client = MongoClient('')
# 팀장mongoDB
# client = MongoClient('')
# 멤버2 mongoDB
# client = MongoClient('')
# 멤버3 mongoDB
# client = MongoClient('')
# 멤버4 mongoDB
# client = MongoClient('')
db = client.dbsparta
@app.route('/')
def home():
return render_template('index.html')
# 멤버님 서브 페이지 동작 관련 코드
@app.route("/myprofile/new", methods=["GET"])
def post_profile():
return render_template('sub.html', member_id = id)
@app.route('/sub1')
def sub1():
return render_template('sub.html')
@app.route('/sub4')
def sub4():
return render_template('sub4.html')
# 멤버
# B : 데이터 보내기
@app.route("/subA4", methods=["GET"])
def subA4():
profiles_data = list(db.profiles.find({'name':"멤버"},{'_id':False}))
return jsonify({'result': profiles_data})
# 방명록 저장하는 곳(멤버)
@app.route('/guestbook', methods=['POST'])
def guestbook_post():
name_receive = request.form['name_give']
comment_receive = request.form['comment_give']
doc = {
'name' :name_receive,
'comment' : comment_receive
}
db.guestbook_comments.insert_one(doc)
return jsonify({'msg': '방명록이 등록되었습니다!'})
# 방명록 mongDB에서 index.html로 데이터 전송(멤버)
@app.route("/guestbook", methods=["GET"])
def guestbook_get():
all_comments = list(db.guestbook_comments.find({},{'_id':False}))
#db.user.find 에서 users 바꿔야한다 일단 강의대로 fan으로 저장함
#fan -> guestbool_comments 로 변경
return jsonify({'result': all_comments})
# mac 사용자는 포트5001로 변경하세요.
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
<write3.py>-개인 DB
from pymongo import MongoClient
client = MongoClient('개인몽고DB URL')
db = client.dbsparta
doc = {
'name':'멤버 이름',
'about_me':'https://chun-k.tistory.com/',
'photo':'멤버 이미지 URL',
'imoge':'💍',
'Q1':'사용할 줄 아는 언어',
'Q2':'자신의 장점',
'Q3':'MBTI',
'Q4':'TMI'
}
db.profiles.insert_one(doc)
# OOO님 본인 데이터 저장해주세요. (위에 주소 본인DB로 변경 한 후 테스트하기)
# DB에 profiles가 없어서 오류가 나면 이 코드 주석 풀고 터미널 실행하기 (더미데이터)
'🏕️내일배움캠프 > 📂팀원 소개 페이지 만들기(23.05.15)' 카테고리의 다른 글
귀여운 Spring A반 5조 모음 :) (0) | 2023.05.19 |
---|---|
팀원 소개 페이지 5일차-코드 발표 및 회고 (1) | 2023.05.19 |
팀원 소개 페이지 3일차-멤버 소개 git으로 코드 합치기 (0) | 2023.05.17 |
팀원 소개 페이지 2일차-멤버 소개 html/css/onclick (2) | 2023.05.16 |
팀원 소개 페이지 1일차_S.A.와 와이어프레임 제작 (0) | 2023.05.15 |