2022. 11. 4. 23:26ㆍ기록/TIL
[TIL] Today I Learned - 221104
미니 프로젝트 마무리 과정 중 배운 것
각종 구현 메서드
각 팀들의 기본 CRUD 메서드를 제외한 좀 인상 깊었던 기능들을 정리해 보려고 한다. 평소에 웹을 통해 지내다 보면 다 쓰던 기능들인데 정작 팀과 내가 해당 메서드를 구현하려 하니 주어진 부분만 집중을 하느라 시야를 넓게 보지 못한 것 같다.
- 팀페이지로 되돌아가는 버튼 - GET
- 댓글 별 좋아요 누르는 기능 - UPDATE
- 어떤 작성자가 글을 썼는지 검색하는 기능 - GET
- 수정 - UPDATE
추후에 기능을 구현하고 새로운 기능을 생각해낼 경우를 위해 생각의 범위를 넓게 해보려고
계속 인지해야겠다는 생각을 가지게 되었다.
구현 메서드들 및 구현 과정에서의 오류 정리
- 팀페이지로 되돌아가는 링크
개인 페이지에 존재하는 팀명, onset of Spring을 누르면 기존 팀페이지로 돌아가는 방식
<div onclick="location.href='/t'" class="intro">
<h2>onset of Spring🌼</h2>
</div>
- 댓글 찾는 기능
개인 페이지에 존재하는 댓글 중에서 이름을 검색하여 그 사람이 쓴 댓글을 찾는 기능
데이터 베이스의 find를 이용하였고 프론트 단에서 값을 입력받아 백엔드로 전달 후 해당 값으로 db조회 후 반환
// 프론트단에서 검색하는 부분을 구현해본 메서드
function search_privateComment() {
let name = $('#name2').val()
console.log(typeof name)
$.ajax({
type: 'GET',
url: '/team/i2/search',
data: {'name': name},
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 id = rows[i]['id']
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>
<button onclick="del_privateComment(${id})" type="button" class="btn btn-outline-dark">삭제</button>
<input type="text"
</div>
</div>`
$('#search').append(temp_html);
}
}
});
}
#검색을 하는 메서드
@app.route('/team/i2/search', methods = ["GET"])
def private_search():
name = request.form.get('name',False)
print(name)
comments = list(db.i2.find({"name": name}, {"_id": False}))
return jsonify({'comments': comments})
다만 해당 메서드의 경우 html에서와 javascript, 그리고 python에서의 변수의 지정 이름이 동일하지만
지속해서 오류가 발생하였다.
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'name'
오류에 맞춰 해당 경우에 대해 찾아보았고 해당 경우에 대한 해결책을 찾았지만 내 경우에 적용이 되지는 않았다.
# 수정 후 코드
@app.route('/team/i2/search', methods = ["GET"])
def private_search():
name = request.form.get('name',False)
print(name)
comments = list(db.i2.find({"name": name}, {"_id": False}))
return jsonify({'comments': comments})
이는 request.form.get에서 get에 해당하는 코드와 주석이다
def get(self, key, default=None, type=None):
"""Return the default value if the requested data doesn't exist.
If `type` is provided and is a callable it should convert the value,
return it or raise a :exc:`ValueError` if that is not possible. In
this case the function will return the default as if the value was not
found:
>>> d = TypeConversionDict(foo='42', bar='blub')
>>> d.get('foo', type=int)
42
>>> d.get('bar', -1, type=int)
-1
:param key: The key to be looked up.
:param default: The default value to be returned if the key can't
be looked up. If not further specified `None` is
returned.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the default value is returned.
"""
수정이 된 코드에서는 요청된 key가 존재하지 않으면 기본값을 반환하는 경우가 되며
그에 따라 값을 반환하던지 아니면 기본적으로 defalut로 설정이 된 값으로 변환이 된다.
예를 들어 위의 코드 같은 경우 default를 False로 설정하였기에 키 안에 값이 존재하지 않으므로
False를 출력하게 되고 해당 값을 통해 이 메서드로 리턴되는 값이 존재하는지 안 하는지에 대해 알 수 있게 된다.
Default값을 따로 지정하지 않는다면 해당 값은 None을 반환하게 된다.
비록 내가 바라는 대로 깔끔하게 해결은 되지 않았지만
다른 경우에 해당 오류가 또 발생할 수 있기 때문에 기입을 하게 되었다.
추가 221105
해결방안을 찾았다. 튜터님의 도움을 받아서 문제의 해결 방안을 찾게 되었고 그 접근 방식을 체화해보고자
해당 과정을 정리하고자 한다. 다만 해당 과정을 통해 접근하는 과정에서 잘 모르고 있던 부분들은 해결이 된 후
해당 키워드를 검색하여 개념을 알아보면서 알아갔다.
과정에 필요한 간단한 개념과 트러블 슈팅과정을 통해 정리해보겠다.
필요 개념
- HTTP 상태코드 && 페이로드
- 쿼리 파라미터
- 클라이언트에서 서버로 데이터 전송
HTTP 상태코드 && 페이로드
[HTTP] HTTP 상태 코드 (tistory.com)
query parameter - 쿼리 파라미터
해당 이미지는 구글에 쿼리 파라미터를 검색 후 보이는 url 창이다.
이때? 글자 뒤에 존재하는 파라미터를 쿼리 파라미터라고 하며 여러 개의 조건을 연결할 때 &을 붙여서 표현이 된다.
해당 쿼리 파라미터는 문제를 해결하면서 접근할 때 필요한 부분이다.
클라이언트에서 서버로 데이터를 전송
HTTP를 사용하여 클라이언트에서 서버로 데이터를 전송할 때 크게 두 가지 방법이 존재한다.
- 쿼리 파라미터를 통한 데이터 전송
- GET
- 미니프로젝트 내부의 전체 방명록 조회 기능에서 먼저 사용
- 정렬 필터(검색)
- 구현하고자 했던 search기능
- GET
- 메시지 바디를 통한 데이터 전송
- POST, PUT, PATCH (PUT / PATCH의 경우는 아직 이를 사용해 본 적이 없다. 추후에 직접 사용하게 된다면 해당 TIL에 작성할 예정이다.)
- 회원가입, 상품 주문, 리소스 등록, 리소스 변경
search 메서드를 구현하고 진행할 때 처음에는 GET방식으로 구현을 하려고 진행을 하였다.
기존 전체 방명록 조회의 경우에서 이름을 입력받아 해당 이름을 DB에서 조회하고 반환하는 방식으로 생각했기에 쉽게 구현할 수 있다고 생각했다. 다만 해당 경우에서 위에 언급된 문제가 발생하였었다.
해결 방안
문제 접근과정 (trouble shooting)
1. 상황을 파악하기
- search 함수를 구현 후 실행을 했지만 html에서 JavaScript로 전달은 되지만 JavaScript에서 플라스크 서버로는 전달이 되지 않는 상황
- 데이터는 쿼리 파라미터 형식으로 전달 완료
- 다만 서버에서는 데이터를 받아오지 못하는 상황
2. 문제가 발생할만한 지점
- HTML / 자바스크립트 / 파이썬 플라스크 서버에서 주고받는 데이터 {key : value} 중 key 값의 오타로 인한 연결 미흡
- 데이터 형식 지정(String / int)
- mongo DB atlas 사이트에 들어가서 해당 데이터 베이스를 확인한 결과 숫자 아이디를 기입했지만 String으로 저장 완료
- 전달 방식의 차이(GET / POST)
- 내 생각 : GET의 방식으로 기존에도 전체 방명록을 구현했었기에 전달 방식은 문제없었을 것이라 판단
- 튜터님 생각 : 쿼리 파라미터를 json의 형태로 받아오기에 전달 방식의 차이로 인해 해당 상황 발생
- 원인을 상정하여 검증하기
- 전체 방명록 기능 구현 시 해당 상황을 겪어봤기에 이 경우라 판단을 하였지만 해당 x
- mongo DB atlas 사이트에 들어가서 해당 데이터 베이스를 확인한 결과 숫자 아이디를 기입했지만 String으로 저장 완료
- 쿼리 파라미터를 json의 형태로 받아오기에 전달 방식의 차이로 인해 해당 상황 발생
- 튜터님 시점 접근방법
1,2번의 문제가 아니란 것을 확인한 후 3번의 방법을 해결하기 위해 접근
상태코드가 500인 것을 보고 이는 클라이언트에서의 문제가 아닌 서버 내부에서의 문제인 것을 파악
또한 페이로드 상의 전달 형식이 쿼리 파라미터인 것을 확인
실제로 파이참 콘솔에 뜬 오류 메시지
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'name'
쿼리 파라미터로 데이터를 받기에 데이터 형식의 선택의 문제라 판단했으며 해당 경우 2가지의 방법이 존재
- 데이터의 전송방식을 Form으로 받고 전송 메서드를 POST로 변경
- 데이터의 전송 메서드를 GET을 유지하고 db에 조회할 수 있는 키값을 받아오는 것을 Form이 아닌 파라미터로 변경
1의 경우 : ajax 및 플라스크 내의 전달 타입을 POST로 수정
# JavaScript ajax
$.ajax({
type: 'POST',
url: '/team/i2/search',
data: {'name': name}, success: function (response) {}
})
# 파이썬 플라스크
@app.route('/team/i2/search', methods = ["POST"])
def private_search():
# name = request.args.get('name')
# name = request.form['name'] # Form 방식의 수정 전 코드
name = request.form.get('name', False)
print(name)
comments = list(db.i2.find({"name": name}, {"_id": False}))
return jsonify({'comments': comments})
2의 경우 : Form의 형태로 데이터를 받아오는 것이 아닌 파라미터 형태로 데이터를 받아올 수 있도록 코드 수정
// JavaScript
function search_privateComment() {
let name = $('#name2').val()
console.log(typeof name)
$.ajax({
type: 'GET',
url: '/team/i2/search',
data: {'name': name}, success: function (response) {}
})
}
# 파이썬 플라스크
@app.route('/team/i2/search', methods = ["POST"])
def private_search():
name = request.args.get('name')
# name = request.form.get('name', False)
print(name)
comments = list(db.i2.find({"name": name}, {"_id": False}))
return jsonify({'comments': comments})
현직 개발자분의 입장에서 주어진 조건을 하나씩 제거해나가면서 본인이 알고 계시는 지식을 바탕으로
문제를 풀어나가는 부분이 매우 인상적이었다.
해당 과정 중에 중간에 제가 이해하면서 넘어가버린 부분이 있을 수도 있지만 문제에 접근하면서 풀이하시던 과정을
복습하여 다음에 문제가 생겼을 때 적용해보려고 노력하려고 합니다.
'기록 > TIL' 카테고리의 다른 글
[TIL] Today I Learned - 221108 (0) | 2022.11.08 |
---|---|
[TIL] Today I Learned - 221107 (0) | 2022.11.07 |
[TIL] Today I Learned - 221103 (0) | 2022.11.03 |
[TIL] Today I Learned - 221102 (0) | 2022.11.02 |
[TIL] Today I Learned - 221101 (0) | 2022.11.01 |