[거북이반] 웹개발 기초 2강

Yungsang Hwang·2022년 5월 21일
0

웹개발 - 거북이반

목록 보기
2/2
post-thumbnail

🏃‍♂️ 내일배움캠프 거북이반 웹개발기초 2강

🚩회원가입 기능

👷‍♂️ BACKEND - SignUp

Request

회원가입에 필요한 사용자 아이디, 비밀번호 받기

data = json.loads(request.data)
id_receive = data['email']
pw_receive = data['password']

API Progress

🧩 라이브러리 : flask_bcrypt

flask_bcrypt를 사용하여 request.data로 받은 비밀번호를 암호화 해 데이터베이스에 저장한다

👨‍💻 Bcrypt 해싱

pw_hash = bcrypt.generate_password_hash(pw_receive)

👨‍💻 이메일 형식 조회

OOOO@XXXX.com 으로 구성된 이메일 형식인지 아닌지를 조회한다
1. @ 이 포함되어 있는가
2. . 이 포함되어 있는가

👨‍💻 이메일 중복 조회

데이터베이스에 저장되어 있는 같은 값의 이메일이 있다면 데이터베이스에 저장하지 않고 실패 메시지만 돌려준다.

# -- 이메일 조회 --
        if '@' in id_receive:
            if '.' in id_receive.split('@')[1]:
                # -- 중복 조회 --
                if db.user.find_one({'id': id_receive}):
                    msg = '이미 존재하는 아이디입니다.'
                    print(msg)
                    return jsonify({'msg': msg}), 203
                    
                else:
                    doc = {
                        'id': id_receive,
                        'pw': pw_hash
                    }
                    db.user.insert_one(doc)
                    msg = '회원가입이 완료되었습니다.'
                    print(msg)
                    return jsonify({'msg': msg}), 201
            else:
                msg = '이메일 형식이 아닙니다.'
                print(msg)
                return jsonify({'msg': msg}), 202
        else:
            msg = '이메일 형식이 아닙니다'
            print(msg)
            return jsonify({'msg': msg}), 202

Response

얼럿으로 보여줄 메시지와 스테이터스 코드를 리턴한다

🙋‍♂️ html status code

status code는 일반적으로 200, 404, 500 등으로 파이썬 콘솔에서 확인할 수 있다.
리턴 값으로 코드를 같이 보낸다면 javascript에서 쉽게 제어할 수 있다!

return 값, 스테이터스 코드 와 같은 형식으로 보낸다!

👩🏻‍🎨 FRONTEND - SignUp

async function

비동기함수로 요청이 들어올 때만 작동하도록 하기
아래의 모든 기능은 이 async 함수 안에 위치한다!

async function handleSignUp() {
	// 내용
}

send http response data

http 통신 방식으로 보내는 데이터는 헤더, 바디 등으로 나뉘어 전송할 수 있다.
헤더는 브라우저 주소에 나타나며, 바디는 드러나지 않는다고 판단하고 작성했다. (정확한 정보가 아니다)

const signUpData = {
    email : document.getElementById('floatingInput').value,
    password : document.getElementById('floatingPassword').value
  
}

document.getElementById 메서드를 이용해 특정 ID 값을 가진 태그의 html 엘리멘트를 가져온다. 그리고 그 값의 value만을 다시 골라낸다

-> 이게 무슨말이냐면, 브라우저에서 회원가입 창에서 입력한 아이디 비밀번호 문자열을 가져온다는 뜻이다!

그리고 이메일과 비밀번호로 나누어 딕셔너리 형태로 값을 담는다.

fetch api

fetch api는 프론트엔드와 백엔드를 연결해주는 javascript ajax api로, 바닐라 자바스크립트를 사용할 때 지금까지 내가 사용했던 제이쿼리 ajax call를 대신할 것이다.

const response = await fetch('http://127.0.0.1:5000/signup, {
   method: 'POST',
   body: JSON.stringfy(signupData) 
                             })

awati fetch 는 async 비동기 함수를 쓴 것과 마찬가지로 함수가 호출되면 작동해야 하는 비동기라는 속성을 가지고 있기 때문에 작성했다고 생각한다!(정확하지 않음)

fetch 안에는 연결될 서버 API 주소를 작성해야 한다. 그렇지 않을 경우 CORS 에러가 출력될 것이다.

method는 서버와 맞춰주고, 보낼 데이터 형식은 Requset 데이터가 보이지 않는 POST 타입의 속성을 고려해서 body에 담아서 보낸다.

signupData는 딕셔너리 형식인데, json데이터가 아니라면 제대로 서버로 전달되지 않는다. 따라서 JSON.stringfy 메서드로 변환해서 보내야 한다!

response receive

서버와 통신한 뒤 백엔드 API를 통해서 되돌아온 값을 활용해 프론트에서 어떻게 보여줄 지를 정한다.

회원가입의 경우 아이디와 비밀번호를 넣어 요청하면, 아이디의 이메일 형식 조회, 중복 조회를 거쳐 데이터베이스에 암호화하여 저장한 뒤 성공/실패 메시지를 돌려줄 것이다.

response_json = await response.json()

if (response.status == 201) {
   alert(response_json['msg'])
   window.loaction.replace('http://127.0.0.1:5500/frontend/login.html)
}

if (response.status == 202) {
   alert(response_json['msg'])
}

ajax 통신을 통해서 보내고 다시 돌려받은 데이터를 json 형식으로 변수로 초기화 해서 받아놓는다. 단! 위의 내용과 마찬가지로 데이터가 돌아와야 받아야 하니 await 를 선언한다.

백엔드 API에서는 각각 처리를 한 내용에 따라서 각기 다른 status 값을 가지고 있다. 각 상황마다 부여된 스테이터스 코드 마다 조건으로 보여주는 값을 달리해 출력한다.

201번은 데이터가 모두 제대로 들어가서 회원가입 처리를 해줘야 하는 경우에 해당한다.
성공 메시지를 얼럿으로 띄워주고, 로그인 화면으로 이동하게 해주는 window.location.replace 메서드를 사용한다.

🚩로그인 기능

👷‍♂️ BACKEND - LogIn

뼈대

@app.route('/login', methods=['POST'])
def sign_in():
	return jsonify ({'msg': 'msg'})

Request

로그인에 필요한 사용자 아이디, 비밀번호 받기

data = json.loads(request.data)
id_receive = data.get('email')
pw_receive = data.get('password')

API Progress

데이터베이스 조회

사용자 데이터베이스에 리퀘스트로 받은 값과 일치하는지 확인하기

find_user = db.user.find_one({'id': id_receive})

    if find_user:
    	# 생략
    else:
        msg = '존재하지 않는 아이디입니다'
비밀번호 체크

사용자의 아이디와 맞는 데이터요소 중 비밀번호까지 일치하는지 확인하기
해싱된 비밀번호와 문자열을 체크하는 Bcrypt의 check_password_hash 메서드를 이용한다

if bcrypt.check_password_hash(find_user['pw'],pw_receive):
    # 생략
else:
    msg = '비밀번호가 일치하지 않습니다'
JWT Payload

아이디 비밀번호 조회까지 마쳤다면 JWT 토큰을 내보낸다
토큰은 페이로드(전송할 데이터)에 담아 인코딩해서 클라이언트로 보낸다. 일종의 통행증 같은 셈이다.
토큰에 사용되는 시크릿키는 보안을 위해서 오픈소스 공간에 노출하지 않는다. 배포를 위해 공유했다면 시크릿키를 변경하자

# -- payload --
payload = {
    'id': find_user['id'],
    'exp' : datetime.utcnow() + timedelta(seconds=60*60*2)
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

Response

아이디, 비밀번호를 모두 확인하고 jwt를 인코딩했다면 토큰 값을 그대로 클라이언트로 보낸다
스테이터스 코드는 201로 지정했다.

return jsonify ({'msg': '로그인에 성공! 환영합니다 ^~^','token': token}),201

스테이터스 202,203은 사용자 요청 값이 잘못되었을 경우에 돌려주는 상황에 해당한다
202번은 비밀번호 조회 조건에서 else 분기에 사용한다
203번은 데이터베이스 조회 조건에서 else 분기에 사용한다

msg = '비밀번호가 일치하지 않습니다.'
   return jsonify({'msg': msg}), 202
msg = '존재하지 않는 아이디입니다.'
   return jsonify({'msg': msg}), 203

👩🏻‍🎨 FRONTEND - LogIn

async function

async function으로 비동기로 함수를 호출한다. 왜냐하면 로그인을 시도해야 데이터가 있을 테니까!

<button onclick="handleSignIn()"></button>
async function handleSignIn() {

}

request data set up

API로 보낼 데이터 값을 변수에 담아 준비한다. API명세를 기준으로 작성한다면 금상첨화
딕셔너리 형태로 이메일과 비밀번호를 받는다
document.getElementById().value 로 ID와 맞는 태그의 값만을 저장한다

const signupData = {
        email: document.getElementById('floatingInput').value,
        password: document.getElementById('floatingPassword').value
    }

fetch api

fetch api를 사용해서 ajax 통신을 시도한다
await는 fetch api와 짝궁이라고 생각하면 된다
fetch 안에는 서버에 들어갈 포트 번호를 잘 생각해서 url을 작성한다
데이터를 전송할 http method와 데이터를 넣어줄 headers/body 를 골라 데이터를 넣어준다
데이터를 보낼 값은 json 형식이어야 해서 JSON.stringfy() 메서드를 사용해 데이터를 보낸다

const response = await fetch(`${BE_BASE_URL}/login`, {
        method: 'POST',
        body: JSON.stringify(signupData)
    })

response data

response_json 데이터 받기

request를 json으로 보내고 response로 돌아올 때도 json으로 돌아온다.
돌아온 변수를 .json 메서드를 통해 받는다

const response_json = await response.json()
로컬스토리지에 저장

백엔드에서 클라이언트로 넘어온 토큰을 로컬스토리지에 저장한다.

localStorage.setItem("token", response_json['token'])
window.location.replace

아이디 비밀번호 조회했고, 토큰을 보내줬으니 로그인 페이지 이후로 이동하자
window.location.replace 메서드를 활용해 지정된 url로 보내준다

window.location.replace(`${FE_BASE_URL}/frontend/index.html`)
스테이터스 코드 202,203

실패 코드를 받는다면, response msg를 얼럿으로 띄운다

else if (response.status == 202) {
        alert(response_json['msg'])
    }

🚩토큰 인증 데코레이터

👷‍♂️ BACKEND - authorize, getname

데코레이터 함수

하나의 기능을 구현할 때 마다 사용자 정보확인과 같이 반복적으로 들어가는 코드가 있다면? 매번 작성하기에는 너무나도 비효율적이다. 하나의 함수로 묶어서 간편하게 볼 수 있게 하는 것이 데코레이터 함수다.

함수 선언
def authorize(f):
    # 생략
데코레이터 선언
def authorize(f):
    @wraps(f)
데코레이터 함수 선언

데코레이터 함수 안 인자 값에는 모든 값이 들어올 수 있다는 것을 의미한다

def authorize(f):
    @wraps(f)
    def decorated_function(*args, **kws):
       # 생략
리퀘스트 헤더 검사

권한 인증의 경우 리퀘스트 헤더를 통해서 토큰이 넘어와야 한다.
토큰을 검사해서 값이 없다면 abort(401)을 보내줘야 한다.
abort(401)은 플라스크 라이브러리 abort를 사용한 것으로, 권한이 없으니 미권한 페이지로 강제로 넘겨버리는 것이다.

값이 있다면, 백엔드 API에서 사용할 때 그때그때 매번 적지 않기 위해서 토큰변수에 담아준다

if not 'Authorization' in request.headers:
    abort(401)
token = request.headers['Authorization']

👩🏻‍🎨 FRONTEND - getUserName

profile
하루종일 몽상가

0개의 댓글