브랜디는 여성의류를 판매하는 사이트로 쇼핑몰, 블로그 마켓, 디자이너 브랜드 등이 모여있는 플랫폼이다.
인턴십 기간 동안 의류를 판매하는 셀러들이 사용하는 브랜디 어드민 사이트를 구현하는 과제를 수행했다. 과제를 수행하며 주 2회씩 현업 개발팀장님에게 코드 리뷰를 받았다. 내가 작성한 코드를 발표하고 피드백을 받는 식으로 진행 되었는데, 내 코드를 적용했을 때 실제 현업에서 어떤 문제들이 발생할지에 대해 말씀해주셔서 좋았다.
프론트엔드로 인턴십을 시작했지만 팀장님께서 백엔드 작업도 병행하는 것을 추천하셔서 이번 기회를 통해 API를 작성해보았다. 처음에는 1도 모르는 백엔드 코드를 작성하려니 너무 막막했다.. 하지만 같은 팀 백엔드 분들이 많이 도와주셔서 로그인 API를 작성하는데 성공했다. 그 동안 백엔드와 소통할 때 헷갈리고 문제가 되었던 부분들이 API를 작성하며 많이 이해가 되었다. API를 한 번이라도 작성해본 경험은 앞으로 내가 개발함에 있어 큰 도움이 될 것이라는 느낌이 들어서 더욱 좋았다.
마지막 주에는 개발 팀장님과 각각 다른 팀에서 근무하고 계시는 브랜디 실무자 두 분 앞에서 최종 발표를 했다. 밤새 작업도 하고 ppt도 만들면서 나름 열심히 했었는데, 실제 발표할 때는 긴장을 너무 해서 준비한 것들도 잘 발표 못 하고 내가 무슨 말을 했는지 기억도 잘 안나서 아쉬웠다. 만드는 것만큼 그 것을 전달하는 것 또한 중요하다는걸 느꼈다. 다음부터는 대본도 만들고 코딩 뿐만 아니라 발표 연습도 열심히 할 것이다..!
그리고 개발팀장님과 1:1 면담을 했는데, 이런저런 좋은 이야기들을 많이 해주셨다. 그 중 가장 기억에 남는 것은 내가 나의 단점에 대해 말해달라고 했을 때 고민의 깊이가 얕다고 말씀해주셨다. 단순히 UI&UX 구현하는 것을 넘어 어떤 구조, 어떤 기술이 적합한지에 대해 좀 더 고민해보라는 말씀이셨다. 나의 고민이 얕은 이유는 기본기가 부족해서라는 생각이 들어 코어 자바스크립트
책을 통해서 자바스크립트 기본기를 공부하고 있다!!
이번 인턴십 기간 동안 작성한 코드 중 가장 기억에 남는 부분은 로그인 API를 작성한 코드이다. 가장 어렵기도 했고, 그 만큼 성취감도 컸던 것 같다.
API 아키텍처는 레이어드 아키텍처를 적용하여 controller-service-model로 작성했다.
controller에서 클라이언트 측에서 받은 정보를 인자로 service 함수를 실행시켜, 해당 결과 값을 return 해주었다.
service에서는 클라이언트에게 전달해주어야 할 데이터들을 가공하였고, model에서 직접 db에 접근하여 필요한 데이터를 꺼내오도록 쿼리를 작성했다.
완벽하게 메서드 하나하나를 다 이해하지는 못 했지만, 백엔드에서 데이터를 어떤 흐름으로 처리하는지와 로그인 성공 시 서버에게 받기만 했던 토큰이 어떻게 만들어지는지 경험해볼 수 있어서 좋았다.
로그인 API를 작성하며 내가 백엔드에 흥미가 있다는 것을 알게 되었고, 나중에는 node.js를 공부해서 백엔드 분야를 더욱 깊게 도전해 볼 생각이다.
controller
@account_bp.route('/login', methods=['POST'])
@validate_params(
#들어온 파라미터들을 유효성 검사
Param('sellerId',JSON,str,required=True),
Param('sellerPassword',JSON,str,required=True),
)
def login(*args):
try:
db_connection = get_connection()
credential = request.json
authorized = account_service.login(credential, db_connection)
# 로그인 성공
if authorized:
# service에서 해당 함수를 실행하여 sellerId와 일치하는 id와 password를 가져온다
user_credential = account_service.get_user_info(credential['sellerId'], db_connection)
account_type_id = user_credential['account_type_id']
account_id = user_credential['id']
# service의 generate_access_token 함수를 통해 생성한 token을 token에 저장
token = account_service.generate_access_token(account_id, account_type_id)
print(token)
# service에서 nav_list와 button_list를 가져오는 함수를 실행
nav_list = account_service.get_nav_and_button(account_type_id, db_connection)
# token과 account_type_id을 클라이언트에게 전송
return jsonify({
'access_token' : token,
# 마스터일 경우 1, 셀러일 경우 2
'account_type_id' : account_type_id,
'nav_list' : nav_list
})
# 로그인 실패 - 비밀번호 불일치
else:
message = internal_code_sheet['S107']
return jsonify(message), (message['code'])
# 로그인 실패 - sellerId가 db에 없을 경우
except NotFoundError as e:
message = internal_code_sheet[e.code]
return jsonify(message), (message['code'])
# 로그인 실패 시 - 그 외 에러 발생
except Exception as e:
return e, 401
finally:
db_connection.close()
Service
# pw 일치 여부 확인 service 함수
def login(self, credential, db_connection):
account_name = credential['sellerId']
password = credential['sellerPassword']
# account_dao에서 해당 함수를 실행하여 account_name과 일치하는 id, password, type_id를 가져 옴
user_credential = self.account_dao.get_user_info(account_name, db_connection)
# 클라이언트가 입력한 pw와 db에 저장된 pw를 비교하여 true or false로 반환
authorized = bcrypt.checkpw(password.encode('UTF-8'), user_credential['password'].encode('UTF-8'))
return authorized
# 토큰 생성 service 함수
def generate_access_token(self, account_id, account_type_id):
payload = {
'account_id' : account_id,
'account_type_id':account_type_id,
'exp' : datetime.utcnow() + timedelta(seconds = 60 * 60 * 24)
}
token = jwt.encode(payload, SECRET_KEY, 'HS256')
return token.decode('UTF-8')
Model
def get_user_info(self, account_name, db_connection):
"""
accounts table에서 account_name과 일치하는 id, password, account_type_id를 반환
"""
with db_connection.cursor() as cursor:
query = """
SELECT
id,
password,
account_type_id
FROM accounts
WHERE account_name = %(account_name)s
"""
cursor.execute(query, {'account_name': account_name})
row = cursor.fetchone()
# account_name과 일치하는 데이터가 db에 없을 경우 NotFoundError 발생
if row is None:
raise NotFoundError('S000')
return {
'id' : row['id'],
'password' : row['password'],
'account_type_id' : row['account_type_id']
}
인턴십 기간 동안 로그인 한 유저가 마스터인지, 셀러인지 구분하기 위해 전역 상태 관리의 필요성을 깨닫고, 이를 해결하기 위해 React-Redux를 사용하기로 했다. Redux에 대해 공부하고 초기세팅까지는 완료했지만, 시간이 부족해서 직접 적용까지 못해본 것이 아쉽다. 하지만 이번 기회를 계기로 막연하게 어렵다고만 느꼈던 Redux의 흐름을 이해하게 되어서 좋았다. 어렵다고 느끼는 것들도 차근차근 공부해보면 내가 할 수 있다는 자신감도 얻었다!
로그인 실패 시, 회원가입 실패 시 등 각각의 에러 처리를 하지 못한 점. 인턴십을 하면서 상황별 에러 메세지를 서버에서 보내주면 프론트 측에서 처리를 했어야 했는데, 이 부분도 시간이 부족하여 구현하지 못했다. 페이지를 구현함에 있어 한 페이지를 제대로 만드는 것 보다는 많은 페이지를 만드는데 초점을 두어서 발생한 문제라고 생각이 됐다. 다음부터는 한 페이지를 구현하더라도 제대로 만들도록 노력해야겠다!
로딩 처리를 하지 못한 점. 상품 필터링 등과 같은 작업을 할 때 클라이언트 측에서는 해당 기능이 처리 되고 있는지 알 수 없기 때문에 필터링 된 데이터가 다 들어오기 전까지는 로딩 화면을 처리해주어야 한다. 앞으로는 사용자를 배려하는 코드를 작성하도록 많은 고민이 필요하다고 느꼈다.