😂 거북이반 인스타 클론코딩
- 인스타그램 클론코딩을 진행하며 구현력이 좋지 않은 것도 사실이지만, 내가 작성했던 코드말고 다른 종류의(?) 코드로도 구현을 해보고 싶었다. 실제로 이번 거북이반 인스타 클론코딩을 튜터님의 지휘 아래에서 다시 진행하면서 많은 점이 달라졌다.
😭 회원가입 기능 구현
- 이메일
- 이메일을 검증하는 부분에
정규식
을 사용해야 하나 아직 하지 못했다.
- 이메일이 비어있거나 이메일 형식이 아닌 다른 형식의 문자열이 쓰인 경우에는 회원가입을 하지 못하도록 막았다.
- 비밀번호
- 이메일과 마찬가지로 빈 값을 넣으면 회원가입을 하지 못하도록 막았다.
- 해쉬 알고리즘으로 비밀번호를 암호화하여 db에 넘기도록 하였다.
jasonify
부분의 201
은 임시 방편으로 작성하였다. 회원가입 버튼만 누르거나 빈 값이어도 프론트 부분에서는 회원가입이 된 것으로 인정되어 status code가 어떤 경우에도 200으로 나와 201로 성공한 경우를 한정하였다.
@app.route("/signup", methods=['POST'])
def sign_up():
data = json.loads(request.data)
email_receive = data.get('email')
domain = ['naver.com', 'gmail.com', 'daum.net']
if '@' in email_receive:
if email_receive.split('@')[1] in domain:
if db.user.find_one({'email': email_receive}):
return jsonify({'msg': '이미 존재하는 이메일입니다.'})
else:
email = email_receive
else:
return jsonify({'msg': '도메인을 확인해주세요.'})
elif email_receive == '':
return jsonify({'msg': '이메일 칸이 비었습니다.'})
else:
return jsonify({'msg': '이메일 형식으로 입력바랍니다.'})
password_receive = data.get('password')
if password_receive == '':
return jsonify({'msg': '비밀번호를 입력해주시기 바랍니다.'})
else:
password_hash = hashlib.sha256(
password_receive.encode('utf-8')).hexdigest()
doc = {
'email': email,
'password': password_hash
}
db.user.insert_one(doc)
return jsonify({'msg': 'Signup success! Welcome!'}), 201
- javascript의
비동기 처리
를 다룰 수 있는 방법 중 하나인 async/await
를 사용하였다.
const backend_base_url = 'http://127.0.0.1:5000'
const frontend_base_url = 'http://127.0.0.1:5500/frontend'
async function handleSignup() {
const signupData = {
email: document.getElementById('floatingInput').value,
password: document.getElementById('floatingPassword').value
}
const response = await fetch(`${backend_base_url}/signup`, {
method: "POST",
body: JSON.stringify(signupData)
})
console.log(response)
if (response.status == 201) {
alert('회원가입에 성공하였습니다!');
window.location.replace(`${frontend_base_url}/login.html`);
} else {
alert('회원가입에 실패하였습니다. 다시 시도해주세요!');
window.location.reload();
}
}
😭 로그인 기능 구현
- 사용자가 로그인에 필요한 값을 입력하고 백앤드로 정보를 보낼 때 사용자의 정보가 누출되지 않도록하기 위해
POST 형식
을 사용하였다.
- 사용자의 이메일과 비밀번호를 받은 후, 해당 정보가 user db에 있는지 확인 하는 과정을 통해 회원가입 유무 확인이 가능하다.
- ObjectId를 받아와 id로 사용하였다. 진짜 지금까지 저 값을 찾을라고 별 짓을 다했는데, 이렇게 쉽게 받아오니까 현타가 쎄게 왔었다.
- 토큰 발급을 위해 사용자의 중요한 정보를 제외한 값들을 payload에 넣어주고, 이를 암호화하였다.
- 토큰값을 가져야 할 사용자의 db에 넣어주기 위해
update
를 사용하였다.
- 여기서도 로그인에 성공한다면 status.code의 값을 201로 주어 프론트에서의 문제를 해결하였다. 추후 수정할 사항 중 하나이다.
@app.route("/signin", methods=['POST'])
def sign_in():
data = json.loads(request.data)
my_email = data.get('email')
password_receive = data.get('password')
my_pw = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
my_signin = db.user.find_one({'email': my_email, 'password': my_pw})
print(my_signin)
if my_signin is not None:
payload = {
'id': str(my_signin['_id']),
'email': my_email,
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=60 * 60 * 24)
}
print(payload)
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
db.user.update_one({'email': my_email}, {'$set': {'token': token}})
return jsonify({'msg': 'Login success! Welcome!', 'token': token}), 201
else:
return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})
- 저번 프로젝트보다 난이도를 쉽게 가져가기 위해 토큰의 값을 로컬 스토리지에 저장하는 방법을 채택하였다.
setItem과 getItem
을 통해 각각 로컬 스토리지에 저장할 값을 넣고 뺄 수 있다.
async function handleSignin() {
const signinData = {
email: document.getElementById('floatingInput').value,
password: document.getElementById('floatingPassword').value
}
const response = await fetch(`${backend_base_url}/signin`, {
method: "POST",
body: JSON.stringify(signinData)
})
console.log(response)
response_json = await response.json()
console.log(response_json)
localStorage.setItem("token", response_json.token)
if (response.status == 201) {
alert('로그인에 성공하였습니다!');
window.location.replace(`${frontend_base_url}/index.html`);
} else {
alert('로그인에 실패했습니다. 재시도해주세요!');
window.location.reload();
}
}
😭 로그인 기능 구현
- 프론트 단에서 headers에 넣어 보내준
Authorization 값
에서 토큰값을 받아온 후, 토큰값이 있다면 해당 값을 복호화하여 원하는 값을 사용하고 있다.
- 로그인 기능에서 payload에 같이 넣어준
_id
값을 사용해 사용자의 정보를 가져온 뒤, 우리가 아이디로 사용하고 있는 email
값을 리턴값으로 돌려주고 있다.
@app.route("/getuserinfo", methods=['GET'])
def get_user_info():
token = request.headers.get('Authorization')
if not token:
return jsonify({'msg': 'check your token again...'})
print('1', token)
user_payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
print('2', user_payload)
user_info = db.user.find_one({'_id': ObjectId(user_payload['id'])})
print('3', user_info)
return jsonify({'msg': 'success', 'email': user_info['email']})
- 해당 함수를 사용하는 데 에러가 나 완벽하게 구현하지 못했다.
- getName 함수를 사용해 메인 페이지에 백앤드에서 리턴해준 사용자의 이메일을 화면에 띄워주려 했으나 아래와 같은 에러가 나 해결하지 못하고 있다.
async function getName() {
const response = await fetch(`${backend_base_url}/getuserinfo`, {
headers: {
'Authorization': localStorage.getItem('token')
}
})
console.log(response)
response_json = await response.json()
console.log(response_json)
const usermail = document.getElementById('usermail')
usermail.innerText = response_json.email
return response_json.email
}