Git Product home page Git Product logo

gabialibrary's Introduction

Anurag's GitHub stats Solved.ac Profile

gabialibrary's People

Contributors

didrlgus avatar gabiacloud avatar gal2o avatar h2nry avatar ygh12 avatar

Watchers

 avatar

gabialibrary's Issues

ff

1. 주제선정 및 기술스택 선정과정

  • 저희는 g혜의 숲 도서관리 서비스 개발을 주제로 선정하였습니다.
  • 현재 g혜의 숲 도서관리 시스템이 이미 갖추어져 있지만, 해당 서비스를 저희만의 방식으로 만들어보고 싶었고, 현재 만들어진 서비스보다 더 개선된 시스템을 구축할 수 있을 것이라는 생각에 도서관리 시스템 개발을 주제로 선정하게 되었습니다.
  • 주요 기술 스택으로는 spring boot, vuejs, mysql을 선택했습니다.
    • vuejs, mysql은 필수사항이었고, 빠르고 쉽게 정돈된 프론트를 구현하기 위해 Vuetify 프레임워크를 사용하여 프론트엔드를 구현하였습니다.
    • backend framework은 선택사항이었는데, 저희는 spring framework를 선택했습니다.
    • 이유는 spring은 거대한 오픈소스 생태계를 지원하면서, 저희 둘다 spring에 대한 경험이 있는 상태였기 때문에, spring이 2달정도의 기간동안 가장 quality 높으면서, 의미있는 결과를 도출할 자신이 있는 framework이라 판단되어 선택했습니다.

2. 아키텍처 설계과정

  • 먼저 저희는 아키텍처를 설계하는 과정에서 monolithic, microservice 아키텍처에 대한 고려를 했습니다.

monolithic architecture

  • monolithic architecture란 하나의 애플리케이션 내에 모든 서비스 로직이 포함되는 통짜구조입니다.
    image

    • monolithic 장점
      • 개발이 단순 (repository 하나 clone 받아서 개발하면 되니까..)
      • 배포 단순 (java기준 jar or war file 하나만 배포하면 되니까...)
      • Scale-out 단순 (서버 하나 복사하면 되니까...)
    • monolithic의 한계
      • monolithic의 한계는 주로 서비스가 커지는 경우에 파생되는 이슈들입니다.
      • 무겁다. (서비스가 커질 수록 IDE에서 실행하는데 점점 무리가 옵니다, application 시작시간 오래걸림, 생산성이 떨어짐)
      • 테스트 및 배포하는데 점점 많은 시간이 요구됩니다.
      • 코드 관리가 점점 어려워짐
      • 기술스택을 바꾸기가 어렵습니다. (바꾸려면 새로운 언어 및 프레임워크 전면 재개편 해야하는데 이는 굉장한 노력과 시간이 들어가는 문제)

microservice architecture (MSA)

  • MSA란 하나의 시스템을 여러개의 독립된 서비스로 나누고, 이 서비스들을 조합함으로서 기능을 제공하는 아키텍쳐 디자인 패턴 입니다.
    image

  • MSA 장점

    • 크고 복잡한 애플리케이션이라도 서비스 단위의 빠른 개발과 독립적인 배포, 확장이 가능함 (msa의 목적)
    • 각각의 서비스는 규모가 작아 관리하기 수월하다.(유지보수 용이, 버전 업데이트나 새로운 기술을 실험하고 도입하기 용이)
      • IDE도 느려지지 않으므로 개발 생산성도 올라감
  • MSA 단점

    • 운영 복잡도가 가중될 수 있습니다.
    • 분산 transaction에 의한 데이터 일관성을 유지하는데 많은 고민과 어려움이 있을 수 있습니다.

MSA 도입 이유?

  • 사실, 현재 인턴과제로 수행하는 과제의 성격이 앞으로 서비스가 굉장히 커질 것을 대비해 microservice의 도입이 불가피한 상황은 아니라고 생각합니다.
  • 하지만 저희는 두 방식 중 MSA 방식을 선택했습니다.
  • 저희는 이번 인턴 과제에서 '성장'이라는 키워드를 가장 중점적으로 두었습니다.
  • 그렇기에 보다 더 많은 성장을 이루기 위해서는 이전에 많이 해본 방식인 monolithic 방식의 architecture로 서비스를 개발하는 것보다는 MSA를 이용하여 서비스를 개발해보는 경험을 하는 것이 더 많은 성장을 이룰 수 있을 것이라 판단하여 MSA를 선택했습니다.

MSA 초기 아키텍처 설계

  • MSA를 효율적으로 설계하기 위한 여러 디자인 패턴들이 있습니다.

    • service discovery, edge server, central configuration, circuit breaker... 등
  • 그중에서 저희는 edge server (엣지 서버), service discovery (서비스 검색) 패턴의 필요성을 느껴 적용시켰습니다.

  • 먼저 api gateway의 필요성 입니다.
    image

    • MSA에서 api-gateway가 없다면?
      • 각각의 서비스마다 인증/인가 등 공통된 로직을 구현해야 하는 번거로움
      • 수많은 API 호출을 logging 하여 관리하기 어려움
      • client에서 여러 마이크로 서비스의 모든 주소값을 갖고 있어야 함
      • client에게 내부 마이크로 서비스의 end point가 노출돼 보안에 취약해질 수 있음
      • 이러한 문제들은 뒷단에서 동작하는 마이크로 서비스의 갯수가 많아질수록 더욱 심화됨
  • discovery server의 필요성 입니다.
    image

    • MSA를 구성하는 모든 service들은 service registry를 공유하여 각 service들이 어느 위치에 존재하는 지 파악할 수 있어야 합니다.
    • 또한, 서버가 새롭게 시작되면 그것을 감지하여 해당 정보를 자동으로 추가하고, 서버가 종료되면 자동으로 해당 정보를 삭제할 수 있따면 보다 더 효과적으로 MSA를 구축하는 것이 가능합니다.
    • 이것을 가능하게 하는 것이 discovery server 입니다.
  • Netflix OSS

    • MSA를 가장 잘 구축했다고 알려져있는 netflix는 MSA를 구축하면서 파악한 pattern들을 구현한 오픈 소스를 공개했고, Spring 진영에서는 이러한 넷플릭스 OSS를 wrapping해 출시한 spring cloud라는 project를 제공했기 때문에 이를 사용.
      • edge server는 spring cloud netflix zuul을 discovery server는 spring cloud netflix eureka를 적용
      • 또한, eureka 내의 neflix ribbon이라는 기능을 이용하면, client side loadbalancing이 가능하여 service 수준의 scaling이 가능
    • 즉, MSA를 보다 효과적으로 구현하기 위해 저희는 netflix zuul, eureka, ribbon을 적용시켰습니다.
  • 다음은 저희 도서관리 서비스의 MSA 초기 설계도 입니다.
    image

  • 서비스를 나눈 기준

    • 각 기능들의 coupling, cohesion를 기준으로 service를 나누었습니다.
      • api gateway, discovery server와 그 외 총 7개의 microservices
    • cohesion이 높은 기능들은 최대한 하나의 서비스로 묶었고, coupling이 낮은 기능들은 최대한 작게 나누려고 노력했습니다.
    • 위와 같이 각 component는 자체 storage를 가지며 다른 컴포넌트와는 REST API 통신을 통해 상호 작용하는 구조입니다.

3. 개발단계에서 어떤 고민들이 있었고 어떻게 해결했는지

1. MSA에서의 인증처리에 대한 고민 및 적용과정

  • 먼저, Monolithic에서의 가장 일반적으로 인증처리 방식으로는 session 방식이 있습니다.
    image

  • 다만, microservice에서는 각각의 service가 독립적인 환경을 가지고, service 단위의 유연한 scaling이 발생합니다.

  • 그렇기 때문에 monolithic에서 일반적으로 했던 session 인증방식을 이용한다면, 각 서버마다 session의 sync를 유지하는 과정에서 많은 기술적 어려움과 비효율이 발생합니다.
    image

  • 그렇다면, 다음과 같이 중앙 집중식 session storage를 사용한다면 이전에 발생했던 issue를 어느정도 방지할 수 있습니다.

    • session storage는 빠른 조회를 위해 redis나 memcached와 같은 in-memory DB가 일반적입니다.
      image
  • 물론, 해당 방식도 좋은 방식이지만 매번, 인증을 요구하는 요청을 수행할 때마다 session storage 로의 network 통신이 발생하는 것은 불가피해지며, DB에 의존적인 상황이 됩니다. 그렇기에 해당 방식이 최선의 방식은 아니라고 생각했습니다.

  • session 방식의 인증처리에 한계를 느꼈고 token based 인증처리를 고려했습니다.

  • token based 인증처리 중에서도 jwt based 인증처리는 이전 방식에서 발생하는 DB의존성을 issue를 해결할 수 있으며 네트워크 cost를 최소화 시킬 수 있는 방식입니다.

  • 따라서, 저희는 msa에서 가장 적절하다고 판단되는 jwt 인증방식을 통해 인증처리 기능을 구현했습니다.

  • 다음은, 저희가 설계한 jwt based 인증 아키텍처 입니다.
    image

2. 도서대여 API에서 발생했던 이슈 및 해결과정

  • 먼저, 기존에 개발했던 도서대여 API는 다음과 같은 로직으로 동작했습니다.
    image

  • 하나의 method 내에서 수행되는 작업들입니다.

    • 책 데이터 업데이트
    • 대여 데이터 추가
    • 알람 데이터 추가
    • 도서대여 성공 메일 전송
  • 도서대여 API의 개발을 다음과 같이 마친 이후, 해당 코드에서 issue가 발생할 수 있겠다는 생각이 들었습니다.

@Transactional
public ResponseDto rentBook(...) {
    
    ...

    updateBook();

    addRent();

    addAlert();

    sendRentSuccessMail();

    ...

}
  • 위의 로직에서 발생할 수 있는 issue들 입니다.

    • 요구사항 변동에 맞게 유연하게 대처하지 못하는 구조입니다.
      • 즉, 만약 현재상황에서 요구사항이 변경돼 카카오톡 전송 작업을 추가해야하는 상황이라고 가정한다면, 해당 service method 하단에 카카오톡 전송 로직을 추가해야 합니다. (점점 service method가 heavy해 지고 유지보수 및 관리하기 어려워 질것)
    • 도서대여 API에 작업이 추가될 때마다 해당 API의 resonse 시간은 점점 늘어날 것입니다.
    • 일반적으로 mail전송, sms전송과 같은 서비스들은 외부 라이브러리를 이용하기 마련인데, 이처럼 내가 직접 control 할 수 없는 외부 component에 의해 내 서비스가 영향을 받을 수 있는 구조가 됩니다. 이는 결코 좋은 설계가 아니라고 생각합니다.
  • 위에서 발생하는 문제들의 근본적인 이유는 각 작업들의 coupling이 높다는 것이라고 생각했습니다.

  • 그래서 각 작업들의 coupling을 loose하게 만들기 위해 message queue를 이용한 EDA(Event Driven Architecture) 설계를 고려했습니다.
    image

  • 이전에, 여러 작업들을 수행하던 도서대여 API에는 오로지 mq로 message를 전송하는 producer로써의 역할만을 수행합니다.

  • 그리고 해당 mq에 쌓인 message들을 listening하고 있던 consumer들이 각각의 용도에 맞게 사용하는 식으로 동작하게 됩니다. (kafka 사용 이유)

  • 다음과 같이 중앙에 message queue를 이용한 pub/sub구조를 설계한다면 이전 로직에서 발생할 것으로 예상되는 3가지 문제를 모두 해결할 수 있습니다.

  • 최종적으로 현재 다음과 같은 issue가 발생할 것으로 우려되는 도서 대여 API, 도서 반납 API, 도서 신청 API를 EDA구조로 설계하여 개발 완료했습니다.

3. 분산 트랜잭션 환경에서 발생할 수 있는 데이터 일관성 문제에 대한 고민 및 해결과정

  • MSA에서는 서로 별도의 독립된 DB를 가지기 때문에 transaction 분산에 의한 데이터 일관성 문제가 발생할 수 있습니다.

  • 저희 또한, 리뷰 추가 API에서 데이터 일관성 문제에 대한 issue 발생 우려가 있었습니다.

  • 다음은, 리뷰추가 API 호출 시 추가 및 업데이트 되는 데이터입니다.
    image
    image

  • 먼저 review table에 review 데이터가 하나 추가됩니다. 이후 book table의 review_count, total_rating 값이 업데이트 되는 구조입니다.

  • 이 상황에서 리뷰 추가 API 호출 시 데이터 일관성 문제가 발생할 수 있는 시나리오 입니다.
    image

  • 이와같이, book 내의 review 데이터가 반환된 이후 review service 측에서 장애가 발생하면 review table의 데이터만 rollback이 발생해 review data와 book data간의 데이터 일관성 문제가 발생합니다.

  • 저희는 이러한 일관성 문제를 해결하기 위해 2가지 방식을 고려했습니다.

  • 첫번째 방식

    • 데이터 일관성 문제가 발생하는 근본적인 이유는 book table에 review 관련 data가 들어가있기 때문입니다.
    • 그렇기 때문에 기존에 했던 erd 설계를 변경해서 book table에 있는 review관련 data들을 모두 없애는 방식으로 데이터 일관성 문제를 해결할 수 있습니다.
    • 다만, 이렇게 book table에 review관련 data들을 모두 지운다면, book을 조회할 때마다 같이 띄워주어야 하는 review 데이터를 호출하는 API가 추가적으로 필요합니다.
    • 저희 서비스에서 book list를 조회하는 api는 traffic이 가장 많을 것으로 예상되는 api 중 하나 입니다.
    • 해당 api에 추가적인 API를 호출하도록 하는 설계는 좋지 않은 방식이라고 판단되어 첫번째 방식을 선택하지 않았습니다.
  • 두번째 방식

    • 기존의 설계를 변경 하지 않으면서 MSA에서 데이터 일관성 문제를 해결할 수 있는 대표적인 방식인 SAGA pattern 이라는 design pattern을 고려하였습니다.

    • SAGA pattern은 크게 Choreography와 Orchestration 기법 두가지로 나뉘는데, 저희가 선택한 기법은 Choreography 방법입니다.

    • 상대적으로 구축하기 쉬우며, 복잡한 transaction을 다루는 상황은 아니라고 판단했기 때문입니다.

    • Choreography 방법은 event를 relay하는 방식이며 저희가 구축한 Choreography 기반의 SAGA pattern에 대해서 말씀드리겠습니다.

    • 먼저, 사전 작업으로 review table에 상태값을 저장할 수 있는 컬럼 status를 추가했습니다.
      image

    • 다음과 같이, 해당 컬럼에 PENDING, COMPLETED, CANCELED 총 3개의 status를 저장할 수 있습니다.
      image

    • 이후 다음과 같이, message를 저장할 3개의 topic과 message를 소비할 3개의 consumer를 생성했습니다.

    • 동작과정에 대해 간단히 말씀드리면, review 추가 api 호출 시 review-create-topic으로 message가 전송되고, 해당 토픽을 consuming하고 있던 review-create-consumer가 동작해서 review table에 review data를 'PENDING'이라는 status를 갖도록 저장합니다. 'PENDING' 상태의 data는 임시 데이터로 사용자에게 노출되지 않습니다.

    • 이후, consumer에서의 작업이 성공적으로 처리되면 review-update-of-book-topic에 성공 message를 전송합니다.

    • 이후, review-update-of-book-topic을 consuming 하고 있던 review-update-of-book-consumer가 동작하여 대상 책 데이터 내의 리뷰 평점, 리뷰카운트 데이터를 수정합니다. 해당 작업이 성공적으로 마무리되면 'COMPLETED' 상태값을, 실패하면 'CANCELED' 상태값을 담은 message를 다음 topic인 review-create-result-topic으로 전송합니다.

    • 마지막으로 review-create-result-topic을 consuming 하고 있던 review-create-result-consumer가 동작하여 해당 message를 읽어서 기존에 'PENDING'으로 저장해놓았던 review data의 status 값을 모든 작업 성공 시 'COMPLETED', 작업 실패 시 'CANCELED'로 변경합니다.

    • 결과적으로 'COMPLETED' status를 갖는 review data 만을 정상적인 유효한 data로 판단합니다.

    • 위와 같은 설계를 직접 구축 완료하였고, 분산트랜잭션 환경에서 데이터의 일관성을 보장할 수 있었습니다.

4. 프론트 개발 과정

  • 로그인 세션 유지 방법

    • Vue에서 jwt 토큰을 이용한 세션 유지 방식은 크게 두가지가 있었습니다. localStorage vs browser cookie
    • localStorage 방식은 기존 cookie 방식보다 더 많은 정보를 저장할 수 있지만 스크립트 실행으로 정보를 쉽게 빼낼 수 있다는 단점이 있습니다.
    • 하지만 cookie 방식은 httpOnly, CSRF 방지 Token 속성을 이용하여 이를 방지 할 수 있다는 점에서 보안상 더 좋습니다.
    • 따라서 Vuex Store + cookie를 이용하여 로그인 세션을 유지하도록 하였습니다.
    • 네비게이션 가드를 이용하여 로그인이 되지 않으면 다른 페이지에 접속하지 못하게 하였습니다.
  • 페이지 UI/UX 개발 (시연용)

    • 이전에 Vue로 개발을 할 때에는
      태그 등을 이용하여 정돈되지 않고 완성도가 적어보이는 페이지를 구성했었습니다.
    • Vuetify에서 제공하는 component를 이용하여 깔끔하고 정돈된 화면을 구성할 수 있었습니다.
    • 기존 g혜의 숲에서는 메인페이지에 책정보를 썸네일과 같이 나열만 되어 있는데 좀 더 이목을 끌 수 있는 메인페이지를 구성하기 위해 리뷰가 많은 책, 최근 추가된 책 순으로 책을 보여주고 싶었고,
      공간을 효율적으로 사용하고 싶어서 Caroucel 컴포넌트를 이용하여 책을 볼 수 있게 만들었습니다.
    • 공지사항, 이용안내에서는 불필요하게 페이지를 두개로 구분되어 있다고 생각해서 하나의 페이지로 합쳤고, 글을 볼 때도 페이지 이동이 아닌 모달을 띄워서 내용 확인, 수정 및 삭제가 가능하게 하였습니다.
    • 도서 신청에서는 도서 이름, 저자, 참조 URL을 일일이 입력을 해야 했습니다. 수기로 입력하는 것이 불편하다고 생각되어 자동완성 기능을 추가하였습니다.
    • 네이버 북스 api와 autocomplete 컴포넌트 + debounce 를 이용하여 키보드 입력을 중지하면 실시간으로 자동완성이 되는 기능과 자동으로 도서 신청 정보를 보낼 수 있는 기능을 구현하였습니다.
    • 다양한 컴포넌트 등을 이용하여 UI를 구성하였습니다. (v- data-table, pagination, calender, expandpanel, dialog, card, caroucel)

4. 결론

최종 아키텍처

  • 저희 도서관리 서비스의 최종 아키텍처는 다음과 같습니다.
    image

  • 해당 아키텍처가 갖는 강점은 다음과 같습니다.

    • 크고 복잡한 애플리케이션이라도 서비스 단위의 빠른 개발과 독립적인 배포가 가능합니다.
    • netflix eureka + ribbon의 조합으로 service 수준의 쉬운 scaling이 가능합니다.
    • 특정 서비스의 기술스택을 유연하게 변경할 수 있습니다.
  • @gm1802883, @gm1702846, @gm1902894, @gm2202970, @gm2202978

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.