싸피 1학기 관통 프로젝트

김헌규·2024년 6월 3일
0

프롤로그 - 1학기를 회고하며

2023년까지는 코딩에 코자도 모르는 비전공 문과생이었던 나였지만 우연히 싸피 공고를 본 후 11기로 입과하여 약 5개월 동안 코딩(프로그래밍)에 대해 배우게 되었다. 솔직히 말해서 싸피에 합격한 후에 입과할지 말지에 대해 정말 많은 고민이 있었고 심지어 입과 하루전까지도 잠을 제대로 못 잘만큼 고민을 많이 했었다. 왜냐하면 기존에 국제통상학, 경제학을 복수전공을 하여 졸업을 하게 되었는데 4년동안 배웠던 전공을 뿌리치고 다시 새로운 것에 도전하는 것에 대한 두려움이 가득했었기 때문이다.

어떤 두려움이었냐면 첫째, 남들은 바로 취업준비를 하고 있는데 나는 다시 새로 시작해야 한다는 두려움과 둘째, 코딩을 배워서 과연 내가 취업에 성공할 수 있을지에 대한 두려움들이었다. 하지만 이런 고민을 끝낼 수 있었던 이유가 첫째, 나중에 싸피를 입과하지 않은 것에 대한 후회와 둘째, 내가 싸피에 들어간다고 했을 때 응원해주었던 주변 사람들의 응원에 대한 기대를 저버리고 싶지 않았고 셋째, 도전에 대한 두려움을 없애고 싶다는 이러한 이유들 때문에 싸피에 입과하게 되었다.

싸피에 입과한 후 난생 처음 배우는 파이썬이라는 언어에 파이썬을 활용하여 문제 해결 능력을 위한 알고리즘 문제를 풀고 웹을 만드는 HTML, CSS, Django, Vue3에 대해 정신없이 배우니 어느덧 5월 16일 1학기 관통 프로젝트가 시작되었다.

본문 - 싸피 1학기 관통 프로젝트(금융)

싸피 1학기의 관통 프로젝트는 트랙이 2개로 나뉘어져 있는데 첫째, 영화 트랙과 둘째, 금융 트랙으로 나누어져 있다. 나는 영화보다는 금융쪽이 더 흥미가 있었고 은행권에 취업도 어느 정도 희망하고 있기 때문에 금융 트랙을 선택하였다. 프로젝트는 조별 컨셉에 따라 주어진 필수 요구사항을 진행한 후 자율적으로 추가 기능을 구현하는 프로젝트였다. 음 결론부터 말하자면 첫 프로젝트부터 안좋은 결과물을 내어서 기분이 좋지 않았다.

GG 팀프로젝트 🏢

프로젝트 주제

소통과 편의성에 중점을 맞춘 서비스

팀원 정보 및 업무 분담 내역

팀 구성 : 차상곤(팀장), 김헌규(팀원)

업무 분담 내역

주로 프론트와 백을 나누기 보다는 기능별로 작업을 수행하였지만 실제 일정에 따라 역학 조율을 하여 프론트와 백을 나누어서 한 부분도 있었다.

하지만 기능별로 겹친 비율이 적기 때문에 주로 담당한 파트를 기준으로 작성하였다.

  • 로그인, 로그아웃 기능 (김헌규)
  • 회원가입 (김헌규)
  • 예적금 데이터 50개 이상 가져오기 (김헌규)
  • 예적금 상품 조회 (김헌규)
  • 예적금 상세 목록 조회 (김헌규)
  • 커뮤니티 게시글 기능 (김헌규)
  • 커뮤니티 댓글 기능 (차상곤)
  • 환율 계산 기능 (차상곤)
  • 프로필 기능 (차상곤)
  • 주변 은행 검색 기능 (차상곤)
  • 금융 상품 추천 알고리즘 (차상곤)
  • AI추천 / 검색 서비스 기능 (차상곤)

설계 내용 및 실제 구현 정도

우선 사용한 기술에는 프론트 엔드에는 Vue3와 Bootstrap을 사용하였고 백엔드에는 Django와 DRF를 사용하였으며 챗봇 구현에는 openAI API를 프론트 엔드에서 백엔드로 요청을 보내는 것은 axios를 마지막으로 백엔드에서 가져온 데이터를 중앙에서 저장하고 관리하기 위해 pinia를 사용하였다.

로그인, 로그아웃, 회원가입

  • 로그인, 로그아웃, 회원가입 기능에서는 우선 user모델을 커스텀하기 위해 AbstractUser를 이용하여 상속받은 후 커스텀을 하였고 기본적으로 입력해야하는 아이디, 비밀번호, 이메일 등 이외에 추가적으로 휴대폰 번호, 이름, 그리고 성별, 생년월일, 자산 정보를 입력할 수 있도록 필드를 추가해주었다.
class User(AbstractUser):
    phone_number = models.CharField(max_length=30)
    name = models.CharField(max_length=50)
    gender = models.CharField(max_length=10)
    birth_date = models.DateField(null=True)
    assets = models.IntegerField(default=0)

    def __str__(self):
        return self.username
  • 그 다음 원래 로그인 설정에서 아무것도 안하게 되면 dj_rest_auth의 기본 설정에 의해 아이디, 비밀번호 이외에 이메일을 입력하게 되어있는데 이메일을 입력하도록 하지 않기 위해서 LoginSerializer로 로그인을 커스텀하였다.
class CustomLoginSerializer(LoginSerializer):
    username = serializers.CharField(allow_blank=True)
    email = None
  • 마지막으로 로그아웃은 페이지의 오른쪽 상단에 고정적으로 모든 페이지에서 볼 수 있도록 버튼을 배치하였고 이는 App.vue에서 navBar에 배치하도록 설계하였다. 이것은 position에서 absolute로 위치를 고정시켰다.

메인 페이지

  • 메인페이지에서는 총 3개의 블록으로 구성하였는데 우선 페이지의 중앙에는 프로젝트의 분위기를 나타내기 위하여 carousel를 이용해 3개의 이미지가 일정시간이 지나면 바뀌도록 설계를 하였고 오른쪽 하단의 블록에는 현재의 조회수 순으로 현재 인기글을 출력하도록 구성하였다. 마지막으로 왼쪽 하단 블록에는 챗봇 대화창을 넣었다.

  • 보통의 사이트에는 오른쪽 하단에 고정적으로 작은 버튼을 펼쳐야 보이기 때문에 사용자의 눈에 잘 안보일 수 있기 때문에 메인페이지에 배치를 하여 메인 페이지에 들어오자마자 사용자들이 챗봇의 존재를 바로 확인할 수 있도록 설계를 하였다.

  • 원래는 메인페이지에 공지사항 및 경제 기사 칸도 마련하려고 했으나 일정상 구현하지 못하겠다고 판단하여 아쉽게도 빼고 진행하였다. 이 부분에서 정말 아쉬웠다.

예적금 금리 비교

  • 예적금 금리 비교 기능에서는 먼저 데이터를 가져올 때는 금융상품통합비교공시 API를 통해 요청을 하여 Django의 모델과 DRF의 serializer를 이용해 데이터의 crud를 수행할 수 있도록 구현하였다. 데이터를 금융상품에 대한 리스트와 그 상품에 대한 옵션 리스트가 따로 나누어져 있었기 때문에 각각 따로 불러와서 반복문을 통하여 DB에 저장하였다. 또한 데이터에 NULL 값이 있는 경우가 있기에 NULL이 존재하는 필드에 대해서는 if문을 통하여 0으로 전처리를 해주었다.
# 데이터 저장 뷰 함수
# 정기 예금 baseList와 optionList 저장
@api_view(['GET'])
def deposit_save(request):
    URL = BASE_URL + 'depositProductsSearch.json'
    params = {
        'auth' : settings.API_KEY,
        'topFinGrpNo' : '020000',
        'pageNo' : 1,
    }
    response = requests.get(URL, params=params).json()
    BaseList = response.get('result').get('baseList')
    OptionList = response.get('result').get('optionList')

    for product in BaseList:
        fin_co_no = product.get('fin_co_no')
        dcls_month = product.get('dcls_month')
        fin_prdt_cd = product.get('fin_prdt_cd')
        kor_co_nm = product.get('kor_co_nm')
        fin_prdt_nm = product.get('fin_prdt_nm')
        join_way = product.get('join_way')
        mtrt_int = product.get('mtrt_int')
        spcl_cnd = product.get('spcl_cnd')
        join_deny = product.get('join_deny')
        join_member = product.get('join_member')
        etc_note = product.get('etc_note')
        max_limit = product.get('max_limit')
        dcls_strt_day = product.get('dcls_strt_day')
        dcls_end_day = product.get('dcls_end_day')
        fin_co_subm_day = product.get('fin_co_subm_day')
        
        if max_limit == None:
            max_limit = 0
        
        if dcls_end_day == None:
            dcls_end_day = '없음'

        if not DepositProducts.objects.filter(
            fin_prdt_cd = fin_prdt_cd
        ).exists():
            product_data = {
            'fin_co_no' : fin_co_no,
            'dcls_month' : dcls_month,
            'fin_prdt_cd' : fin_prdt_cd,
            'kor_co_nm' : kor_co_nm,
            'fin_prdt_nm' : fin_prdt_nm,
            'join_way' : join_way,
            'mtrt_int' : mtrt_int,
            'spcl_cnd' : spcl_cnd,
            'join_deny' : join_deny,
            'join_member' : join_member,
            'etc_note' : etc_note,
            'max_limit' : max_limit,
            'dcls_strt_day' : dcls_strt_day,
            'dcls_end_day' : dcls_end_day,
            'fin_co_subm_day' : fin_co_subm_day,
        }
            serializer = DepositProductListSerializers(data=product_data)
            if serializer.is_valid(raise_exception=True):
                serializer.save()
    
    for option in OptionList:
        dcls_month = option.get('dcls_month')
        fin_co_no = option.get('fin_co_no')
        fin_prdt_cd = option.get('fin_prdt_cd')
        intr_rate_type = option.get('intr_rate_type')
        intr_rate_type_nm = option.get('intr_rate_type_nm')
        save_trm = option.get('save_trm')
        intr_rate = option.get('intr_rate')
        intr_rate2 = option.get('intr_rate2')
        deposit_product = DepositProducts.objects.get(fin_prdt_cd=fin_prdt_cd)
        
        if intr_rate == None:
            intr_rate = 0
        
        if intr_rate2 == None:
            intr_rate2 = 0

        option_data = {
            'dcls_month' : dcls_month,
            'fin_co_no' : fin_co_no,
            'fin_prdt_cd' : fin_prdt_cd,
            'intr_rate_type' : intr_rate_type,
            'intr_rate_type_nm' : intr_rate_type_nm,
            'save_trm' : save_trm,
            'intr_rate' : intr_rate,
            'intr_rate2' : intr_rate2,
        }    

        serializer = DepositOptionsListSerializers(data=option_data)
        if serializer.is_valid(raise_exception=True):
            serializer.save(deposit_product=deposit_product)
    return JsonResponse({'message' : '저장완료'})
  • 이러한 원리를 이용해 프론트엔드에서 상품별 비교 페이지로 접속을 하면 onMounted를 이용해 store에 설정해둔 함수를 실행시켜 axios 요청을 통해 DB에 저장을 하도록 하였고 이렇게 저장된 데이터들을 다시 불러와 리스트에 저장을 시켰으며 이 리스트들을 상품별 비교 페이지로 불러와 출력을 하였다.

  • 상품별 전체 조회는 데이터가 많기 때문에 스크롤 바를 구현하여 사용자가 굳이 페이지의 스크롤을 안 내리고 이용할 수 있도록 구성하였고 이자율의 6개월, 12개월, 24개월, 36개월에 해당하는 데이터를 가져올 때 이미 상품과 옵션을 따로 저장을 해놨기 때문에 역참조를 하여 가져오려고 했으나 옵션의 구조상 한 옵션 정보에 여러 기간의 이자율의 정보가 있는 것이 아닌 기간별로 따로 분리 되어 있기 때문에 이를 찾아서 가져오기가 까다로워서 직접 데이터를 가공하여 가져왔다.

from django.db.models.fields.related import ManyToManyField

def to_dict(instance):
    opts = instance._meta
    data = DotDict()
    for f in opts.concrete_fields + opts.many_to_many:
        if isinstance(f, ManyToManyField):
            if instance.pk is None:
                data[f.name] = []
            else:
                data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True))
        else:
            data[f.name] = f.value_from_object(instance)
    return data


class DotDict(dict):
    # obj.key
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            return self.key

    def __setattr__(self, key, value):
        self[key] = value
# 정기예금 base_list 보내기
@api_view(['GET'])
def deposit_base_list(request):
    deposits = list(map(to_dict, DepositProducts.objects.all()))
    options = list(map(to_dict, DepositOptions.objects.all()))
    data = []
    for deposit in deposits:
        # 6개월, 12개월, 24개월, 36개월 이자율
        trm = [0, 0, 0, 0]
        for option in options:
            if option.fin_prdt_cd == deposit.fin_prdt_cd:
                intr_1 = 0
                intr_2 = 0
                if option.save_trm == "6":
                    if option.intr_rate != 0:
                        intr_1 = option.intr_rate

                    if option.intr_rate2 != 0:
                        intr_2 = option.intr_rate2 
                    
                    if intr_1 == 0 and intr_2 == 0:
                        trm[0] = 0

                    else:    
                        trm[0] = round((intr_1 + intr_2) / 2, 2)

                
                if option.save_trm == "12":
                    if option.intr_rate != 0:
                        intr_1 = option.intr_rate

                    if option.intr_rate2 != 0:
                        intr_2 = option.intr_rate2 
                    
                    if intr_1 == 0 and intr_2 == 0:
                        trm[1] = 0

                    else:    
                        trm[1] = round((intr_1 + intr_2) / 2, 2)

                if option.save_trm == "24":
                    if option.intr_rate != 0:
                        intr_1 = option.intr_rate

                    if option.intr_rate2 != 0:
                        intr_2 = option.intr_rate2 
                    
                    if intr_1 == 0 and intr_2 == 0:
                        trm[2] = 0

                    else:    
                        trm[2] = round((intr_1 + intr_2) / 2, 2)

                if option.save_trm == "36":
                    if option.intr_rate != 0:
                        intr_1 = option.intr_rate

                    if option.intr_rate2 != 0:
                        intr_2 = option.intr_rate2 
                    
                    
                    if intr_1 == 0 and intr_2 == 0:
                        trm[3] = 0

                    else:    
                        trm[3] = round((intr_1 + intr_2) / 2, 2)

        for i in range(4):
            if i == 0:
                setattr(deposit, "6", trm[0])
            elif i == 1:
                setattr(deposit, "12", trm[1])
            elif i == 2:
                setattr(deposit, "24", trm[2])
            else:
                setattr(deposit, "36", trm[3])

        data.append(deposit)
    
    return Response(data=data, status=status.HTTP_200_OK)
  • 이러한 방식으로 데이터를 가공하여 데이터를 보내니 내가 원하는 형태의 깔끔한 데이터가 프론트쪽으로 전달이 되었다.

  • 상품 상세 조회에서는 ModelSerializer와 view 함수를 통해 상세 조회를 할 수 있도록 설계하였으며 프론트 엔드에서는 기존에 목록을 조회할 때 사용했던 store에 저장되어있는 각 상품별 리스트를 가져와 find를 통해 값을 찾아서 출력을 하였다. 그 다음 가입하기 기능을 구현하기 위해 백엔드에 포트폴리오 모델과 serializer를 만들어 사용자가 선택한 상품이 저장되도록 구현을 하였다. 그리고 중복 가입을 막기 위해 store에 포트폴리오 리스트를 만들어서 포트폴리오 데이터를 저장한 후 이 리스트를 다시 컴포넌트로 가져와 가입하려는 상품의 데이터가 리스트에 있으면 이미 가입된 상품이라고 alert를 띄우고 없으면 가입에 성공되었다고 alert를 띄웠다. 또한 가입하기 버튼은 store에 저장된 로그인 정보를 이용해 로그인이 되어 있을 경우에만 가입하기 버튼이 들 수 있도록 설계를 하였다.

  • 시간이 조금 더 있었다면 도전과제인 메일 서비스를 구현할 수 있었을텐데 그것을 구현하지 못하여 아쉬웠다.

커뮤니티

  • 커뮤니티 기능으로는 기본적인 게시글 조회, 생성, 삭제 수정 및 댓글, 삭제 기능을 django와 vue3를 통해 구현하였으며 좋아요와 대댓글은 기본적으로 명세서에 주어진 과제들을 먼저 구현하고 이후에 진행하려고 하였으나 디테일한 부분을 같이 진행하려고 하는 바람에 일정이 밀리게 되어 구현하지 못했습니다.

  • 커뮤니티의 카테고리로는 사용자들이 자유로운 주제로 작성할 수 있도록 자유 게시판, 사용자들끼리 재테크 정보를 공유할 수 있도록 하는 재테크 공유 게시판, 그리고 문의사항 등을 받기 위해 고객과의 소통 게시판으로 나누었습니다. 추가로 글을 쓰는데에 좀 더 편의성을 제공하기 위해 자유게시판 관련 글을 쓰다가 다시 주제를 바꾸어 올리고 싶을 때를 대비하여 카테고리 선택 항목을 추가로 배치해 주제를 쉽게 변경할 수 있도록 구현하였습니다.

  • 그리고 본인이 작성한 게시글의 삭제와 수정 그리고 댓글 삭제를 구현하기 위해 store에 저장된 로그인 정보를 이용해 게시글의 user_id와 댓글의 user_id를 비교하여 같으면 띄우고 같지 않으면 보이지 않도록 하였습니다.


여기서부터는 차상곤 교육생이 진행한 파트입니다.

프로필 페이지

  • 프로필 페이지에서는 기본 정보와 가입한 상품의 포트폴리오 정보를 볼 수 있도록 구현하였고 차트 라이브러리를 사용하려고 했으나 일정 이슈로 구현을 하지 못했습니다. 다만 회원가입을 할때에 입력 받은 자산정보를 바탕으로 포트폴리오에 담은 상품 별로 12개월 이자율의 정보를 가져와 현재 나의 자산에서 이자율을 통해 계산을 하여 1년 후 나의 자산 정보를 띄우도록 구현하였습니다.

  • 그리고 회원 정보를 수정할 수 있도록 게시글 수정과 같은 로직을 통해 구현하였습니다.

환율 계산기

기능 : 한국 화폐 <-> 외국 화폐로의 환전

  • onMounted 기능을 활용해서 환율 페이지로 접속시 환율 데이터 API요청을 보내어 DB에 저장하게 됩니다. 이 때, 이미 정보가 존재한다면 다시 요청을 보내지 않게 처리해주어야 오류가 발생하지 않습니다.

  • input 이벤트가 장착된 input 태그의 값에 변동이 생길 때마다 select의 value 값으로 지정된 국가화폐코드 (미국의 경우 USD)를 이용해서 get axios요청을 보내고, Back-End에서 Exchange 모델의 환율을 조회합니다.

  • 환율과 variable routing을 통해 보내진 돈의 금액을 곱(또는 나눗셈)을 통해서 계산된 값을 반환해서 출력합니다.

어려웠던 점 : 한국 화폐 <-> 외국 화폐로의 환전 기능만을 구현했습니다. 위의 기능을 구현했다면 외국 화폐 -> 외국 화폐의 기능도 구현하기는 매우 쉽지만(그냥 한국 돈으로 환전하는 한 번의 과정만 더 거치면 됩니다.), 부정확도가 매우 높을 것 같아서 굳이 안 하는 게 낫다고 판단해서 하지 않았습니다. value 값은 실제로 "디르함 (AED)"와 같은 형태로 적혀 있었는데, 디르함은 한국어로 읽었을 때의 화폐단위인데, 달러,엔,유로 등과 같이 널리 알려진 화폐의 단위 외에는 대중들이 모를 가능성이 높기 때문에 단위에 같이 적어주었어야 했습니다.
그런데 이 부분이 조금 문제가 발생했습니다. 띄어쓰기를 같이 varial routing에는 활용이 바로 할 수 없었는데 인코딩(띄어쓰기->%20)-디코딩(%20->띄어쓰기)이 필요했습니다.
그러나 검색을 해보니 JS문법에서도 파이썬의 split과 비슷한 문법이 있었고, 그 덕분에 AED만을 추출해서 변수로 넘길 수 있었고, value 값은 그대로 화폐의 단위로 표시할 수 있었습니다.

근처 은행 검색

기능 : 원하는 위치의 특정(또는 모든) 은행 검색 기능 & 현재 위치 근처의 특정(또는 모든) 은행 검색 기능

동작 방식

  • 데이터 초기화 및 컴포넌트 마운트 : 데이터 초기화 및 컴포넌트 마운트될 때 카카오 맵 스크립트를 로드하여 지도를 초기화합니다.

  • 시/도, 시/군/구 선택 시 목록 업데이트 : 사용자가 시/도를 선택하면 해당 시/도의 시/군/구 목록을 업데이트 합니다. 사용자가 시/군/구를 선택하면 해당 시/군/구의 읍/면/동 목록을 업데이트합니다.

  • 카카오 스크립트 로드 : 카카오 맵 API 스크립트를 동적으로 로드하고, 로드가 완료되면 지도를 초기화합니다.

  • 지도 초기화 : 지도를 초기화하고, 장소 검색 객체를 생성합니다.

  • 지정한 위치의 은행 검색 : 사용자가 선택한 옵션들을 기반으로 검색 쿼리를 생성하고, 해당 위치의 은행을 검색합니다. 이 때, 검색 결과 중에서 은행 이름이 포함된 항목만 필터링하여 표시합니다.

  • 현재 위치 근처의 은행 검색 : 사용자의 현재 위치를 가져와서 해당 위치를 중심으로 반경 2km이내의 은행을 검색합니다. 검색 결과를 지도에 마커로 표시하고, 마커를 클릭하면 인포윈도우(은행의 이름을 표시하는 창)를 추가합니다.

  • 지도에 표시된 모든 마커와 인포윈도우 제거 : 새로운 검색을 하면 기존에 지도에 표시돼 있던 마커와 인포윈도우를 모두 제거합니다.

어려웠던 점

  • 카카오 맵 API 가이드를 따라가면서 구현했기 때문에 크게 어려움을 없었습니다. 다만 남들과는 조금 다르게 모든 시/도, 시/군/구, 읍/면/동에 대한 정보를 검색에 활용하려고 했기 때문에 해당 정보를 구하는 것이 조금 까다로웠습니다. 이런 단순 노가다성 작업은 GPT에게 요청하였고, 수십 번의 요청을 해서 겨우 얻어냈습니다. 그래서 다른 조원들에게도 나누어 주었습니다.

챗봇기능

기능 : 메시지 입력 및 전송 & 메시지 저장 및 표시 & Open API 호출

동작 방식

  • 메시지 입력 및 전송 : 사용자는 텍스트 입력란에 메시지를 입력하고, Enter 키를 누르거나 '전송' 버튼을 클릭하여 메시지를 전송합니다.

  • 메시지 저장 및 표시 : 입력된 메시지는 messages 배열에 저장되고, addMessage 함수를 통해 화면에 표시됩니다.

  • OpenAI API 호출 : sendMessage 함수는 사용자가 입력한 메시지를 OpenAI API에 전달하여 챗봇의 응답을 가져옵니다. fetchAIResponse 함수고 API 호출을 처리하며, 응답 데이터를 받아와 addMessage 함수를 통해 챗봇의 응답을 저장하고 표시합니다.

  • 채팅 인터페이스 : 메시지들을 스크롤 가능한 형식으로 표시했고, 구분이 쉽게 색깔을 주었습니다.

  • 프롬프트 : system(챗봇의 동작 방식과 성격을 설정하는 시스템 메시지), user(사용자가 입력한 메시지), assistant(챗봇이 응답하는 메시지), 'role'과 'content'라는 두 가지 속성으로 챗봇이 어떻게 응답해야 할지를 결정(또는 학습 시키는 것), 챗봇의 역할과 GG Bank의 서비스 (투자 상품 비교, 환율 계산, 은행 찾기 등)를 설명하도록 설정

데이터베이스 모델링 (ERD)

금융 상품 추천 알고리즘에 대한 기술적 설명

기능 : 사용자의 예금 및 적금 상품을 추천

동작 방식

  • 사용자 입력 및 API 요청 전송 성별, 나이, 연봉, 자산, 저축 성향을 입력하고, 검색 버튼을 눌러서 django 서버로 API 요청을 보냅니다.

  • 사용자의 위험 감수 성향 평가 : 사용자가 입력한 데이터를 바탕으로 점수를 계산하고, 그에 따라 등급을 평가합니다.

  • 금융 상품의 위험 등급 평가 : DB에 저장된 예금 및 적금 데이터를 조회하여 점수를 계산하고, 그에 따라 등급을 평가합니다.

  • 금융 상품 반환 : 사용자의 위험 감수 성향과 동일한 상품들 중 위험도가 낮은 순으로 데이터를 반환합니다.

  • 사용자의 저축 성향에 따른 상품 추천 : 사용자가 선택한 저축 성향(단기, 중기, 장기)에 맞는 개월수의 상품들을 추천해줍니다.

서비스 대표 기능들에 대한 설명

  • 커뮤니티에서 자유 게시판, 정보 공유 게시판, 고객과의 소통 페이지를 나누었지만 글을 작성할 때 하나의 카테고리에 갇혀 있지 않고 중간에 다른 카테고리로 바꾸고 싶으면 바꾸고 작성을 한다면 그 카테고리에 게시물이 post 됨으로써 사용자 편의성을 높혔습니다.

  • 챗봇의 위치를 한눈에 알기 쉽도록 메인 페이지에 배치를 하여 저희의 서비스를 쉽게 접근하고 이해할 수 있도록 편의성을 제공하였습니다.

  • 금융 상품 추천 서비스 : 사람을 성별에 따른 투자 성향 차이, 나이에 따른 투자 성향 차이, 연봉에 따른 투자 성향 차이, 자산에 따른 투자 성향 차이를 점수화 하여 위험도 감수 성향을 평가하였습니다. 그리고 상품들에 대하여 은행의 규모와 안정성, 금리에 따른 안정성, 기간에 따른 안정성을 점수화하여 상품의 위험성을 점수화하여 위험도를 평가하였습니다. 그에 따라 개인의 성향에 맞게 상품을 추천하도록 서비스를 구현하였습니다.

결론 - 후기

후기

  • 처음하는 프로젝트라서 그런지 의욕이 넘쳐서 필수 기능 구현 이외에 몇개의 기능 추가 그리고 디테일한 부분까지 모두 구현을 하려다가 일정이 꼬였으며 또한 이미 구현했던 기능이 다음날이 되면 작동하지 않는 경우가 발생했었기에 일정상 필수 부분만 진행하게 되어 다소 아쉬웠습니다. 그리고 역할을 기능별로 나누다보니 기능간의 연결점들에 의해 충돌이 발생하여 코드가 꼬이게 되어 작동이 안한 경우도 발생하였습니다. 이러한 상황을 잘 해결하기 위해서는 커밋 메시지를 활용을 했어야 했는데 커밋 메시지도 대충 작성한 흔적이 여러 곳에서 발생하였기에 이 부분도 아쉬웠습니다. 마지막으로 처음에 ERD를 어차피 나중에 가면 많이 바뀔수도 있으므로 설계를 제대로 하지 않고 진행하였다는 점에서 프로젝트를 진행하면서 어떤 부분을 진행하고 있는지에 대해 잘 모르게 되는 경우가 발생하였고 매 순간마다 하나씩 설계하면서 진행하다보니 오히려 시간이 많이 걸리게 되었고 비효율적인 ERD를 설계하게 되어 다시 갈아엎느라 시간을 많이 소모하게 되었습니다.

  • 프로젝트를 진행하면서 배우게 된 점은 로그인과 회원가입을 커스텀하는 방법과 OpenAPI를 통해 데이터를 가져와서 저장하고 가공하는 법을 배우게 되었고 기존에 배웠던 게시판 기능을 다시 복습한 계기가 되어 한층 더 실력이 발전되었다고 생각합니다.

  • 이렇게 첫 프로젝트를 진행하면서 아쉬웠던 점과 배우게 된 점을 잘 숙지하여 다음 프로젝트에서는 더욱 발전된 모습으로 진행하도록 하겠습니다.

profile
Happiness is not a destination, it's a way of life.

0개의 댓글