프로젝트 서버리스 컨버터 작업!

김광훈·2021년 11월 2일
2

TIL(Today I Learned)

목록 보기
34/49

프로젝트 컨버터 작업!

오늘 서버리스 실시간 원격 수업이 내일배움캠프 일정상으로 마지막날이였다. 마지막과제로 우리가 2차스프린트까지 Flask로 진행한 프로젝트를 서버리스로 구성해보라는 과제를 주었다. 이미 몇일 전에 프론트 부분을 다 때어서 S3로 정적호스팅화 했기 때문에 백엔드만 복사 붙여넣기하면 될거라는 생각을 했지만 서버리스에 맞춰 백엔드를 수정하다보니 프론트와 호환이 안되는 경우가 있어 프론트도 바꾸고... 정말 엄청나게 삽질을 해가며 컨버터작업을 했다 그 결과 하루종일 작업해서 로그인,회원가입 API 두개뿐 밖에 해결하지 못했다. 내일은 또 스프링을 나가야하기 때문에 이 부분은 잠시 묻어두고 시간을 내서 다시 해결해야될거같다. 그래도 오늘 트러블 슈팅도 하면서 서버리스를 만지다보니 어느정도 자신감도 생기고 삽질한 만큼 공부가 됬다는 느낌이 있어서 좋았다.

오늘 한 일

  • AWS 프로젝트 백엔드 serverless로 컨버터 작업
    • 로그인, 회원가입 API 컨버팅 완료
    • Flask 데코레이티드 기능 1:1 수업 완료...by tutor

Flask -> Lambda

Flask에서 Lambda로 백엔드를 수정할 때 수행했던 작업을 작성해보려고 한다. 추가적인 디테일한 내용은 이후 모든 API를 작성한 후에나 작성될듯 하다...

1. SAM 프로젝트 폴더 생성
AWS 페이지에서 작업하지 않고 SAM 프로젝트에서 작업해서 배포를 진행했다.

sam init

2. EC2에 MongoDB 셋팅!
AWS RDS는 사용하지 않고 EC2에서 인스턴스를 하나 만들어서 그 안에 mongoDB를 설치해서 사용했다.

pymongo 설치
pymongo user,password 설정
인스턴스 27017 포트 개방

3. SAM 프로젝트와 MongoDB 연결
이 부분을 셋팅하는 것이 가장 까다로웠다. 왜냐하면 강의 내용에서는 mySQL을 사용했기 때문에 내가 설정해줘야 하는 값을 직접 찾아봤어야 했다.

# AWS secretmanager 생성 // SecretManager에서 pymongo의 host와 user,password를 가져온다.

def get_secret():
  session = boto3.session.Session()
  client = session.client(
      service_name='secretsmanager',
      region_name="ap-northeast-2"
  )
  get_secret_value_response = client.get_secret_value(
      SecretId='pymongo-secret-01'
  )
  token = get_secret_value_response['SecretString']
  return eval(token)

# Monogo DB 호출 코드 작성 // SecretManager로 받은 값을 통해 인스턴스 mongoDB에 접속

secrets = get_secret()

client = MongoClient("mongodb://{0}:{1}@{2}".format(secrets['user'],secrets['password'],secrets['host']))
db = client.dbrecipe

4. POSTMAN 호출을 통한 테스트와 프론트엔드 테스트
POSTMAN에서는 동작을 하는데 프론트엔드에서는 동작을 안하는 문제가 발생했었다. 원인을 찾아보니 프론트엔드에서 백엔드를 호출할 때 CORS에러가 발생. 대부분은 아래와 같이 return에 header를 달아줌으로서 해결되었지만 그 외에 해결하지 못한 부분들이 있다. 이 부분은 추후에 해결할 예정이다.

return {
          "statusCode": 200,
          'headers': {
              'Access-Control-Allow-Headers': 'Content-Type',
              'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Methods': 'OPTIONS,POST'
          },
          "body": json.dumps({
              "result": "success"
          })
      }

서버리스에서 JWT토큰에 대한 방식 고찰

제목을 거창하게 지었지만 사실 이번에 서버리스 수업을 들으면서 JWT토큰을 어떻게 써야할지에 대해 강의를 들어서 작성하게 되었다. 기존의 Flask로 프로젝트를 진행할때는 서버 측에서 페이지에게 토큰값을 요청해서 그 토큰값을 갖고 로그인 유무를 판단했다.

token_receive = request.cookies.get('mytoken') # 브라우저로부터 토큰값을 요청해서 받아갔음

하지만 서버리스 수업을 들으면서 다음과 같은 방법에 대해 알게되었다.
바로 프론트엔드에서 API를 호출할 때 토큰값을 보내는 것이다. 그리고 이때 토큰은 로컬스토리지에 저장되어있다.

	function(xhr) {
		if (localStorage.getItem('token') != null) {
			xhr.setRequestHeader('Authorization', localStorage.getItem('token'));
		}
  	}
//header의 Authorization 키값에 토큰을 담아 API로 보낸다.

두가지 방식의 차이점은 서버에서 요청하고 쿠키에 저장된다와 브라우저에서 보내고 로컬스토리지에 저장된다라는 점이다.
서버리스에서는 후자의 방식을 사용해야하는데 그 이유는 바로 CORS다.(정말 지긋지긋) 쿠키에 저장된 토큰은 도메인이 다르면 토큰을 서버가 요청할 수 가 없다! 하지만 로컬스토리지에 저장된 토큰은 도메인이 다르더라도 브라우저가 header에 담아 서버로 보내줄 수 있다는 것이다! 즉 서버리스와 같이 프론트와 백엔드의 도메인이 다를 경우 JWT를 쿠키에 담는 방식은 사용할 수 없다는 것이다.

전처리에대한 생각

오늘 전처리에 대한 이야기를 많이 들었는데, 프로젝트 개발을 하면서 진짜 많이 중요하다고 느끼고 있다. 이 작업을 처리하면 코드가 정말 간단해지는데 왜 이제서야 알게된걸까 너무 아쉬웠다.
그것에 대한 코드를 여기에 풀어 볼까 한다.

  • 전체 ajax 코드에 대한 전처리 코드
$.ajaxSetup({ // ajax에서 API를 호출하면 API에서는 이 전처리 코드를 수행을 먼저 실시합니다.
        error: function (jqXHR, exception) {
            switch (jqXHR.status) { 
                case 401:
                    alert("인증 에러!!");
                    break;
            }
        },
        beforeSend: function(xhr) {
            if (localStorage.getItem('token') != null) {
                xhr.setRequestHeader('Authorization', localStorage.getItem('token'));
            } else {
                xhr.setRequestHeader('Authorization', 'anonymous');
            }
        }
    });
  • Flask 전처리 코드
def login_check(f): # flask 함수위에 @login_check을 작성하면 이 코드가 먼저 실행된다.
    @wraps(f)
    def decorated_function(*args, **kwargs):
        access_token = request.headers.get("Authorization")
        if access_token is not None:
            try:
                payload = jwt.decode(access_token, secret, "HS256")
            except jwt.InvalidTokenError:
                return Response(status=401)

            if payload is None:
                return Response(status=401)

            user_id = payload["id"]
            g.user_id = user_id
            g.user = get_user_info(user_id)
        else:
            g.user_id = "비회원"
            g.user = None
            print("access_token is empty")

        return f(*args, **kwargs)

    return decorated_function # 신기하게 함수 그 자체를 반환해준다.

✔생각 정리

짬짬히 시간을 내서 백엔드 API를 서버리스로 전환해보자!

profile
잘 부탁드려요

1개의 댓글

comment-user-thumbnail
2021년 11월 3일

코드랑 내용 꼼꼼하게 정리하셨군요!! 서버리스 강의 주간 고생 많으셨습니다~~

답글 달기