Weekly I learned
2022/01/16
회고록
항해99 첫 날.
사전스터디를 마치고 본격적으로 웹 개발을 배우는 시간을 갖게 되었다.
인생에서 중요한 시간이 될 것 이며, 끝까지 후회 없이 배우고 이 시간을 마무리할 수 있기를 바란다.
심화과정을 선택하였고, 첫주는 미니 웹프로젝트이다.
D반 9조 조장이되었다.
팀원은 나 포함 4명이었으며, 다들 열심히 하고자 하는 모습이 보기 좋았다.
아이디어 회의를 하였고, 투표를 통해 내 아이디어 였던 지식IN overflow를 주제로 프로젝트를 진행하게 되었다.
Stack overflow를 오마주하여 한글로 된 개발자들의 개발 관련 질문에 대한 웹사이트를 만드는 것 말이다.
그렇게 항해 첫 프로젝트가 시작되었다.
- 크게 로그인&회원가입과 메인페이지 글 CRUD 2 갈래로 나눌 수 있었다.
로그인/회원가입 | 메인페이지 글 CRUD |
---|---|
2명 | 2명(나) |
로그인 회원가입 부분을 맡게 되었고,
글의 리스트가 보여지는 index.html을 구현하게 되었다. 팀원은 write.html을 구현하였고 구현이 완료 된 후, 연동하였다.
데이터를 받아오고 찾아오는 부분은 생각보다 어렵지 않게 구글링 하며 해나갈 수 있었는데, 문제는 가져 온 데이터를 현재 페이지에 어떻게 보여지게 처리하냐는 것 이었다.
이미 데이터들을 모두 처리해 놓은 상태의 페이지에서 검색 버튼을 누르면 reload 하면서 검색완료된 자료들만 따로 처리해주는 페이지로 넘어가야하나? 이 페이지에서 기존 데이터를 싹 지우고, 새로 받아온 검색어에 해당하는 데이터만 새로 보여줄 순 없나?
방법이 딱히 떠오르지 않았다가, 받아오게 되면 empty()로 스크립트를 지우고 append()로 스크립트를 새로 그리는 방법을 떠올렸고, 다행히도 해당 방법으로 문제없이 구현이 되었다.
로그인/회원가입 | 메인페이지 글 CRUD |
---|---|
2명 | 2명(나) |
로그인/회원가입 부분과 cookie를 이용한 jwt 연동
로그인과 회원가입의 기능적인 부분과 기본 UI를 완성하셨다.
내가 구성하던 메인페이지, 쓰기, 읽기, 수정페이지와 쿠키 내 jwt의 값을 페이지 이동시 연동하여 보안에 취약하지 않도록 유저의 아이디 값을 이용할 수 있도록 하였다.
JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 토큰을 의미합니다. JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별합니다.
쿠키/세션 방식에 비해 JWT 방식이 갖는 장점은,
첫째 간편합니다.
세션/쿠키 인증은 별도의 저장소 관리가 필요합니다.
JWT 는 발급한 후 검증만 하기 때문에 추가 저장소가 필요하지 않습니다. (StateLess - 상태/정보 저장하지 않음) 이는 서버를 확장하거나 유지, 보수하는데 유리합니다.
둘째, 확장성이 뛰어납니다.
토큰 기반으로 하는 다른 인증 시스템에 접근이 가능합니다.
Facebook, Google, Microsoft 로그인 등은 모두 토큰 기반으로 인증을 하는데, 권한을 받을 수도, 프로필을 써드파티 웹사이트에 제공하도록 허가 할 수도 있습니다.
팀원끼리 서로 구현한 부분을 연동함에 있어, DB에 사용하는 테이블명이나 필드명이 달라 합치는 과정에서 충돌도 일어나고, 수정하는데 시간이 오래걸렸다.
서버를 올려두긴 하였으나, 아직 CRUD가 적용되지 않은 상태라 local DB에 구현을 하고 있었던 우리는 해당 문제에 부딪힐 수 밖에 없는 상황이었다.
구현하기 전에 회의를 통해 DB의 테이블명과 필드명을 정하고 시작하긴 하였으나, user/users라던지 content/contents라던지 s가 들어가고 들어가지 않는 부분에 있어 필드명의 불일치는 코드의 merge 과정에서 충돌이라는 결과를 낳았다.
하지만, 충분한 의사소통을 통해 그리고 충분한 시간을 들여 해당 문제를 해결하였으며 다음에 이런 일이 없어야 겠다.
팀프로젝트 시, 동시에 접근해야 하는 데이터 즉, DB나 서버 부분의 경우 변수명이나 테이블명, 필드명을 확실하게 정해두고 개발한다.
로그인/회원가입 | 메인페이지 연동 | 글 RUD | extends 적용 및 코드정리 |
---|---|---|---|
기능구현완료 | 연동완료 기능구현완료 | 3명(나) | 1명 |
좋아요 기능을 구현하는 것이 생각보다 까다로웠다.
API 하나만을 이용하여 구현하였고, 토글방식으로 눌러져 있다면, 한 번 더 누를시 취소, 안눌러져 있다면, 누를시 좋아요.
댓글의 BD에 좋아요를 누른 유저의 아이디를 리스트로 저장하여, 해당 데이터 내에 접속한 유저의 ID가 있다면, 좋아요를 누른것 으로 간주, thumbs-up의 내부에 색이 채워졌다.
마찬가지로 리스트에 ID가 없다면, thumbs-up의 내부는 비어있는 것으로 구현하였다.
API(Application Programming Interface 애플리케이션 프로그래밍 인터페이스, 응용 프로그램 프로그래밍 인터페이스)는 컴퓨터나 컴퓨터 프로그램 사이의 연결이다.
질문에 답변 댓글을 작성시, 작성유저라면 삭제 및 수정할 수 있도록 구현하였다.
script로 구현한 input 태그 내 수정 가능한 뷰와, 수정 불가능한 그냥 내용을 보여주는 뷰가 겹쳐져 있었다. 수정버튼을 누르면 보기 위한 뷰는 hide(), 수정가능한 뷰는 show()를 이용하여 구현하였다.
어떤 방식으로 좋아요를 누른 유저를 구분할지가 떠오르지 않았다.
먼저, 몽고DB는 하나의 필드에 리스트를 저장할 수 있는지?
있다면, 필드에 명단 리스트를 저장하여 구별하는 방법이 효율적인지?
우선 몽고DB는 하나의 필드에 리스트를 저장할 수 있다.(ㅇ)
효율적인가? 해당 부분은 아직 미지수다.
수정이나 삭제를 누를시, 뷰가 동적으로 변환되는 부분을 구현하기가 어려웠다. 그 부분 하나 때문에 전체 페이지를 reload하는 것은 너무 비효율적이라 생각했고, 해당 부분만 토글되듯이 뷰가 바뀌기를 원했다.
hide(), show()를 이용하여 해당 부분을 어렵지 않게 구현할 수 있었다.
다만, 댓글이 여러개일 경우 모두 같은 id를 갖게 되기 때문에 첫 댓글에만 hide(), show()가 적용이 되었었는데, 이 부분은 뷰의 id 부분에 댓글의 고유값(_id)를 이어 붙여 모든 댓글이 고유한 id를 갖는 방법으로 해결하였다.
프로젝트가 점점 코드량이 많아지면서, 그리고 팀원간에 공동으로 구현하는 부분이 나오게 되면서, 개별 브랜치에서 통합브랜치로 merge하는 과정에서 충돌이 일어나게 되었다.
.gitignore에 .idea 넣기
.gitignore 파일 내부에 git으로 push나 pull을 하게될 경우 무시해도 되는, 혹은 무시해야만 하는 파일을 등록시킨다.(그냥 .gitignore를 메모장으로 열고 .idea 라고 적어면 됨)
개별적인 파이썬의 버전 혹은 경로, 환경설정들이 들어있는 .idea 파일을 제외시켜 충돌을 방지한다.
.html 파일 분리
해당 html 파일 내에, javascript 부분은 별도로 .js 파일을 만들어 분리시키고, css 부분은 .css 파일을 만들어 static 폴더 내 js 및 css 디렉토리에 각 각 분리시켜 import 하여 사용하면, 충돌을 방지할 수 있다.
로그인/회원가입 | 메인페이지 연동 | 글 RUD | extends 적용 및 코드정리 | 이미지 저장 및 로딩 | UI - 수정 | 필수구현부 |
---|---|---|---|---|---|---|
기능구현완료 | 연동완료 기능구현완료 | 기능구현완료 | 구현완료 | 2명(나) | 1명 | 1명 |
이미지 파일 업로드 하는 부분을 구현하자니 javascript와 ajax, python, Flask로 어떻게 업로드가 가능한지에 대한 정보가 많이 없어 쉽지 않았다.
결론적으로,
ajax, javascript, html을 이용한 이미지 파일 업로드 ajax, javascript, html을 이용하여 이미지 파일을 전송하여, 이미지파일을 지정 경로에 저장하고, 경로 데이터를 DB에 저장하였다.
이미지 불러오기는 이미지태그를 이용하려 상대경로로 접근하여 이미지를 불러왔다.
어떤 방식으로 불러와야 하는지 몰랐다. ajax에 Formdata에 포함시키는 것까지는 확인하였으나, 데이터 전달 자체가 되지 않았고, 에러메시지도 명확하지 않았다. 아니 없었다. 그래서 더 어려웠던 것 같다.
Formdata타입 및 getElementsByClassName과 html 태그 내 class를 file로 선언하는 과정, 그리고 이미지 파일을 불러와 파일명을 분리하는 과정 등, 몰랐던 지식이 많아 구현함에 있어 배울점이 많았다.
결정적으로, request.files를 이용하여 불러와야했다.
구현 중 막히는 부분을 구글 검색엔진을 이용하여 검색하고, 주변에 도움을 청해 필요한 새로운 지식을 이해하며 구현하였다.
아래는 구현한 코드이다.
client.js
let fileInput = document.getElementsByClassName("file");
let file = $('#file')[0].files[0]
let filename = file['name']
let form_data = new FormData()
form_data.append('id_give', id)
form_data.append('title_give', title)
form_data.append('content_give', content)
form_data.append('image', file)
form_data.append('image_name', filename)
$.ajax({
type: "POST",
url: "/api/posting",
processData: false,
contentType: false,
data: form_data,
success: function (response) {
if (response["result"] == "success") {
alert("질문이 등록되었습니다.");
window.location.reload();
location.replace("/");
//main page move
}
},
error: function (e) {
alert('fail');
}
})
server.py
image = request.files['image']
extension = image.filename.split('.')
print('extension : ' + str(extension))
today = datetime.now()
mytime = today.strftime('%Yy%mm%dd%H:%M:%S')
filename = f'{mytime}-{extension[0]}'
filename = "".join(i for i in filename if i not in "\/:*?<>|")
filename = filename.strip()
print('filename : ' + filename)
save_to = f'static/images/{filename}.{extension[1]}.jpg'
image.save(save_to)
print('which one')
doc = {
'user_id': id_receive,
'title': title_receive,
'contents': content_receive,
'imageUrl': f'{filename}.{extension[1]}.jpg',
'count': 0
}
알고리즘 주차 시작, D반 7조
모두 파이썬 공부
코드업(code_up) 사이트 내, 기초 100제를 풀어내기
https://codeup.kr/problemsetsol.php?psid=33
python이 익숙하지 않았던 나를 훈련시키느라 힘들었다.
노력할 것.
문자열과 배열
모두 문자열/배열 과제 및 공부
leet-code
가장 긴 팰린드롬 부분 문자열
https://leetcode.com/problems/longest-palindromic-substring/
해당 문제를 부르트 포스 방식으로 모든 경우의 수를 집어넣어 원하는 답을 얻긴 하였으나, 투 포인터방식을 이용한 expand 방식을 해당 교재에선 추천하였다.
투 포인터 방식에 대한 이해가 더 필요할 것으로 보인다.
나의 solution :
def longest_palindrome(target):
#palindrome 문자열을 저장할 리스트
palinList = []
#start 1씩 증가
for start in range(len(target)):
#end 포인터 -> 문자열 맨 끝으로 위치
end = len(target)
#start ~ end 까지 문자열 길이가 1보다 작으면 더이상 palindrome이 될 수 없으므로 끝냄
if end-start<=1:
break
#start지점은 고정되어있고, end 지점을 1칸씩 앞으로 이동시키며 문자열 case 비교
while (True):
isPalin = True
print('[시작!]')
print('start : ' + str(start) + ', end : ' + str(end))
n=target[start:end]
print('slice : ' + n)
#문자열 짝수, 홀수 구분 로직
odd_even_check = end-start
print('length : ' + str(odd_even_check))
if(odd_even_check%2==0):
#짝수
print('짝수')
odd_even_check = False
else:
#홀수
print('홀수')
odd_even_check = True
i=0
j=len(n)-1
# Target 문자열 내, 맨앞(i)과 맨끝(j)에서부터 한칸식 줄면서 palindrome 비교
while(True):
print('i : ' + str(i) + ", j : " + str(j))
if n[i]!=n[j]:
isPalin=False
break
i+=1
j-=1
if odd_even_check:
print('홀수 진입, odd_even_check : ' + str(odd_even_check))
# 홀수라면,
if i == j:
print('if 진입 : i : ' + str(i) + ', j : ' + str(j))
break
else:
if i == j+1:
break
#Palindrome인 경우, 리스트에 해당 문자열을 추가
if(isPalin):
print('isPalin : ' + str(isPalin))
palinList.append(n)
#end 1씩 감소
end-=1
if end-start<=1:
break
return palinList
if __name__ == '__main__':
n = "oihuyftrdrhrotatorjkuytrerfgh"
palinList = longest_palindrome(n)
maximum_length=max(palinList, key=len)
print('List : ' + str(maximum_length))