[0518] TIL 25일차

nikevapormax·2022년 5월 18일
0

TIL

목록 보기
24/116
post-custom-banner

😂 머신러닝 팀 프로젝트

😭 S.A 작성

머신러닝 팀 프로젝트 S.A


😂 거북이반 인스타 클론코딩

😭 오류 수정

  • 전날 마주쳤던 아래의 오류를 혼자서 수정하지 못하였다.

- 해결 과정

  • 오류 코드를 그대로 구글링 해보았다.

    • stack overflow
    • 해당 내용에서는 jsfiddle이 문제라는 내용을 보았고, 또 링크되어 있는 jsfiddle의 페이지도 들어가 보았으나, 나의 문제에 대입하여 해결을 하지 못하였다.
  • 함수의 이름이 정의되어 있지 못하였다는 에러 메세지의 내용을 보고 현재 해당 함수가 api.js에 존재하고, index.js에서 해당 함수를 호출해 실행하는 것이므로 한 번 api.js 안에서 함수를 실행해보자 생각을 하였다.

    • index.html 파일을 실행하였더니 아래와 같은 오류 메세지가 나왔다.

    • 해당 오류를 해결하고자 또 구글링을 해보았다.

    • 경로를 아래와 같이 재설정해보니 다른 오류가 떴다.

      <script src="../frontend/api.js"></script>

    • 위의 오류를 그대로 구글링해보았다. 아래의 링크에서 script 태그의 위치를 바꿔주면 오류가 해결된다 해서 그대로 시도해보았다.
      cannot-set-property-of-null
      역시 별 효과가 없었다.

    • 오류 메세지의 Cannot set properties of null이라는 내용이 맞다기에는 아래와 같이 response가 아예 오지 않는 것도 아닌 것 같다.

  • 오랫동안 해결하지 못하고 튜터님께 질문을 드렸고, 다시 보다보니 예상하지도 못한 곳에서 오타가 있었다. ㅋ
    오타 체크 꼭하자!!!!!!!!!!!!!!!!!!!!!!!!!!!!!ㅅㅂ!!!!!! ㅋㅋㅋㅋ

😭 오늘 배운 부분

- app.py

  • 데코레이터 함수
    • 데코레이터 함수에 대해 아직 완벽히 이해하지 못해 자세한 설명이 어려워 일단 링크를 첨부하겠다.
      데코레이터 함수
    • 해당 함수를 사용해 사용자 인증이 필요한 부분의 route 아래 부분에 @authorize를 쓰고, 해당 route에 해당하는 함수의 인자로 user를 부여하면 된다.
# decorator 함수를 사용해 사용자의 토큰값 확인
def authorize(f):
    @wraps(f)
    def decorated_function():
        # 만약 Authorization이 헤더 안에 없다면
        if not 'Authorization' in request.headers:
            abort(401)  # 401 에러를 반환
        # Authorization이 헤더 안에 있다면 'token' 변수에 저장
        token = request.headers['Authorization']
        try:
            # 토큰을 디코드한 값을 user에 저장
            # _id, email, exp가 들어있음
            user = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        except:
            abort(401)
        return f(user)
    return decorated_function
  • 메인 페이지에서도 user를 받아 사용자의 정보를 사용할 수 있도록 조치하였다.
@app.route("/")
@authorize
def home(user):
    print(user)
    return jsonify({'msg': 'success'})
  • 사용자의 정보를 받아오는 부분에도 user를 입력받도록 조치하였다.
    • 해당 부분의 사용자의 토큰을 받아와 payload 안의 정보를 확인하는 과정의 코드를 @authorize와 user를 받아옴으로써 없애 코드가 간단해졌다.
    • 또한 user db 안에서 _id를 통해 사용자를 식별해야 하는데, user에 들어있던 값은 문자열이므로 다시 ObjectId로 변환해주는 과정을 거친다.
@app.route("/getuserinfo", methods=['GET'])
@authorize
def get_user_info(user):

    user_info = db.user.find_one({'_id': ObjectId(user['id'])})
    print('3', user_info)

    return jsonify({'msg': 'success', 'email': user_info['email']})
  • 게시글을 작성하는 요청을 처리하는 부분이다. 해당 부분에서는 사용자가 작성한 게시글의 title, content와 함께 user에 들어있는 ObjectId로 타입을 변경한 _id와 email, 그리고 작성 시간을 같이 article 이라는 db 안에 입력해 준다.
    • 주의해야 할 점은 위의 코드도 마찬가지이지만, _id 값의 타입이 현재 어떤 것인지 항상 생각하고 있어야 된다는 것이다.
@app.route("/article", methods=['POST'])
@authorize
def post_article(user):
    data = json.loads(request.data)
    print(data)

    # 여기서의 user는 authorize에 들어가는 인자값
    # user에는 payload로 만들어진 token이 존재하고, payload에는 사용자의 str화 시킨 _id가 존재한다.
    # 이 값을 mongoDB에서 사용하기 위해서는 다시 ObjectId로 변환해줘야 하기 때문에 아래와 같은 작업 진행
    db_user = db.user.find_one({'_id': ObjectId(user.get('id'))})

    # 현재 시각 표시
    now_date = datetime.datetime.now().strftime('%H : %M : %S')

    doc = {
        'title': data.get('title', None),  # 현재는 None 처리를 통해 값이 없더라도 입력 가능
        # 추후 값이 없을 때 에러 처리하는 것을 만들어 활용해보기
        'content': data.get('content', None),
        'user': user['id'],
        'user_email': db_user['email'],
        'time': now_date
    }
    db.article.insert_one(doc)
    return jsonify({'msg': 'success'})

- javascript

  • 메인 화면에 아무 기능도 없었지만, 게시글을 포스팅할 수 있는 화면으로 넘어갈 수 있는 버튼, 작성한 게시글을 메인 화면에 나열해주는 함수를 작성하였다.
  • 해당 부분은 html 파일에 있는 부분이지만 onclick 함수를 새로운 방법으로 활용한 부분이라 작성한다.
    • 기존에는 render_template 기능을 통해 다른 페이지로 많이 넘어가곤 했는데, location.href를 통해 다른 페이지로 넘어갈 수 있다.
<button type="button" onclick="location.href='article_create.html';">게시글 작성하러 가기</button>
  • getElementById를 통해 해당 id 값을 가지는 부분의 value 값을 각 변수에 저장하고, postArticle() 함수의 인자로 넘겨준다.
function handleArticleCreate() {
    const title = document.getElementById('article_title').value
    const content = document.getElementById('article_content').value


    // 여기서 뽑아온 타이틀과 콘텐트의 값을 api.js에 있는 postArticle 함수에 넘기기 위해 함수 실행
    postArticle(title, content)
}
  • title과 content를 인자로 받고, 이 둘을 articleData라는 변수에 저장한다.
    • 해당 값을 body에 실어 Authorization이 담긴 headers와 함꼐 ${backend_base_url}/article이란 주소를 가진 app.py의 함수로 POST 방식으로 데이터를 보내준다.
    • app.py에서는 @app.route("/article", methods=['POST'])를 주소로 가진 곳에서 데이터를 받게 된다.
    • headers에 담긴 Authorization은 앞에서 정의한 authorize 함수로 이동하게 되고, 나머지 데이터는 data = json.loads(request.data)를 통해 JSON 방식의 데이터로 변환되어 나머지 작업들을 할 수 있게 되는 것이다.
    • 만약 해당 작업이 성공하게 된다면 메인 페이지로 옮겨 가게 된다.
async function postArticle(title, content) {
    const articleData = {
        title: title,
        content: content
    }
    console.log(articleData)

    const response = await fetch(`${backend_base_url}/article`, {
        method: 'POST',
        headers: {
            'Authorization': localStorage.getItem('token')
        },
        body: JSON.stringify(articleData)
    })

    response_json = await response.json()
    console.log(response_json)

    if (response.status == 200) {
        window.location.replace(`${frontend_base_url}/`)
    }
}
  • POST 방식으로 백앤드로 게시글의 정보들을 전부 넘겨줬으니, 이것을 받아와 html 상에서 사용자에게 보여주려면 GET 방식을 통해 데이터를 가져와야 한다.
async function getArticles() {
    const response = await fetch(`${backend_base_url}/article`, {
        method: 'GET'
    })

    response_json = await response.json()

    return response_json.articles
}
  • 위의 getArticles 함수를 통해 받아온 값을 articles라는 변수에 지정한다.
    • 원래는 jqeury로 값을 넣어주던 방식이 아닌 javascript의 forEach 함수 안에서 createElementsetAttribute를 통해 새로운 li를 만들고 id 값을 부여하게 된다. 그리고 각 게시글의 제목만 따와서 appendChild를 통해서 ol 안에 각 제목들을 붙여주게 된다.
async function loadArticles() {
    articles = await getArticles();

    const article_list = document.getElementById('articles')
    articles.forEach(article => {
        console.log(article)
        const newArticle = document.createElement('li');
        newArticle.setAttribute('id', article._id)
        newArticle.innerText = article.title;
        article_list.appendChild(newArticle)

    });
}
profile
https://github.com/nikevapormax
post-custom-banner

0개의 댓글