Consistency 

분산 환경을 공부하다보면 CAP를 마주하게 된다. 

 

우선, 분산처리환경의 CAP는 Consistency, Availability, Partition tolerance이고, 트랜잭션의 ACID는 Atomicity, Consistency, Reliability, Isolation, Durability이다. 이글은 각 개념보다는 C에따른 A?.. 위주로 정리하고자 한다. 

 

 

ACID의 C와 CAP의 C는 같은 Consistency지만, 의미가 다른 Consistency이다. 

 

ACID의 일관성(Consistency)는 트랜잭션이 완료되었을 때 기존의 제약을 위반하지 않아야한다는 일관성이고, 

CAP의 일관성(Consistency)는 모든 요청에대해 최신 데이터, 또는 에러를 응답받아야한다는 일관성이다. 

 

ACID의 일관성을 예로 들어보면, 은행에서 출금 후 잔고가 음수이면 안된다. 음수일 경우 트랜잭션은 실패해야하고, rollback되어야 한다. 즉 기존의 제약을 위반하지 않는 데이터로 일관돼야한다. 

 

CAP의 일관성을 예로 들어보면, 부산에서 돈을 출금했을때, 일산에서 잔고를 확인하나, 서울대입구에서 잔고를 확인하나 최신에 업데이트된 값을 보여주어야한다. 

 

 

먼저 CAP이론은 한계가 명확하다.

가장 아래에 참고한 영상 링크를 걸어두었는데 그 영상의 제목은 You don't need CP, you don't want AP, and you can't have CA다.

극단적으로 CAP 중 2개를 만족하는 구조는 불가능, 혹은 장점이 없다. 따라서 더나은 CAP이론의 해석은 "가용성과 일관성은 어느정도 상충관계에 있지만 극단적으로 둘 중 하나만 선택해야하는건 아니다." 이다. 

 

CP와 AP 그 사이 어디쯤으로 선택해야하는데 CP가 얼마나 우선될지, AP가 얼마나 우선될지 그 정도를 선택하는게 중요하다.

 

 또한 파티션이 없는 상황을 설명하지 못한다는 것이다. 파티션이 없는 상황에서도 분산 시스템은 상충하는 특성들이 있고, 장애 상황만큼이나 정상 상황에서 시스템이 어떻게 동작하는지도 중요하다.

 

CAP의 이러한 한계로 PACELC 이론이 나왔는데 마지막에 간단하게 정리해보자.. 

 

 

Eventual Consistency 

분산 시스템을 구축하다보면 C와 A 중 우선순위를 선택할때가 온다.

Eventual Consistency는 고가용성을 보장하는 일관성 모델이다. 

 

jodong2라는 서버와, dongineer라는 서버가 있다고 가정하고, 그 두개는 별도의 데이터베이스를 사용한다고 가정하자. 

 

클라이언트가 요청을 보내고, jodong2라는 서버에 데이터베이스의 데이터가 수정되었다고 하자. 이때 dongieer의 데이터베이스에 해당 데이터와는 내용이 다를 수 있다. 

이때 다른 클라이언트들이 해당 데이터를 읽기위한 요청을 보낸다면 

 

  1. 모든 서버가 동일한 데이터를 갖도록 동기화하는 동안 클라이언트의 접근을 막는 경우 - 가용성 X
  2. 어떤 클라이언트는 최신화된 jodong2의 데이터를, 어떤 클라이언트는 최신화되지 않은 dongineer의 데이터를 보여주는 경우 - 일관성 X

두가지 경우가 있다. 이때 2번이 Eventual Consistency 방식이다. 2번의 경우에는 언젠가 동기화 작업이 완료되면, 모든 클라이언트가 동일한 데이터를 볼 수 있다. 단기적으로는 전체적인 일관성을 잃을지 몰라도, 결과적으로 전체적인 일관성을 보여주는 모델이 Eventual Consistency 모델이다. 

출처 : https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore?hl=ko#what-is-eventual-consistency

위의 그림에서 Node A는 상위노드이고, B,C는 복제본이다. Node A에서 X 데이터에 대해 쓰기 작업이 이뤄지는 중이라도, 혹은 동기화가 되지 않아도 항상 X 데이터를 읽을 수 있다. 이때 B는 동기화되고, C는 동기화 되지 않았을 경우에도, 각자 갖고 있는 데이터중 최신 데이터를 보여준다. 이때 각 데이터는 차이가 발생할 수 있지만, 언제나 접근 가능하다는걸 보여준다. 

인터넷 DNS(도메인 이름 시스템)는 eventual consistency 모델이 사용된 시스템의 예로 잘 알려져 있습니다. DNS 서버가 항상 최신의 값을 반영하는 것은 아니며, 이러한 값들은 인터넷상의 수많은 디렉터리에서 캐싱되고 복제됩니다. 수정된 값을 모든 DNS 클라이언트와 서버에 복제하려면 어느 정도의 시간이 소요됩니다. 하지만 DNS 시스템은 인터넷의 근간을 이루는 요소로 자리잡은 매우 성공적인 시스템입니다. DNS는 가용성이 매우 높으며 엄청난 확장성이 증명되었고, 인터넷 전체에서 수천만 대 기기의 이름 조회를 가능하게 하고 있습니다.

 

 

Strong Consistency 

반대되는(?) 개념으로 Strong Consistency가 있다. 

출처 : https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore?hl=ko#what-is-eventual-consistency

  1. 모든 서버가 동일한 데이터를 갖도록 동기화하는 동안 클라이언트의 접근을 막는 경우 - 가용성 X

 위에서 보여준 예시중 1번과 관련있는 Consistency 모델로 기본적인 관계형 데이터베이스가 그 대표적인 모델이다. 

Strong Consistency 모델은 모든 클라이언트에게 동일한 데이터(일관성)를 보여주기 위해 가용성을 어느정도 포기한 모델인데, 관계형 데이터베이스는 데이터베이스의 여러 인스턴스가 항상 동일한 데이터를 갖도록 한다. 이때 동기화 작업 중 lock이 걸릴 수 있으며 이는 가용성에 문제가 생긴다. 따라서 설계하기 나름이지만, 기본적인 관계형 데이터베이스는 일관성을 우선시한 대표적인 모델이다. 

 

 

 

 

PACELC

CAP이론은 위에서 말한 한계점이 있다. 

PACELC는 CAP이론을 대체하기 위해 나온 이론이다. CAP이론은 정상 상황을 서술하지 못한다는 문제를 해결하기 위해 나왔다. 서버가 Partition 된 상황이라면 A(Availability) 와 C(Consistency)를 골라야하고, E(Else) L(latency) 와 (Consistency)를 골라야한다. 

 

 분산 DB에서 파티션(Partition)일때 Availability를 선택하여 모든 DB에 반영하기보다는, 접근 가능한 노드에만 반영하면 PA, 반대로 모든 DB 반영되는걸 기다리며 Consistency를 선택한다면 PC이다. 

 정상상황 (Else) 일 때는 빠르게 처리하기 위해 몇몇 DB에만 반영하며 Latency를 선택하면 EL, 모든 DB에 적용하여 Consistency를 선택하면 EC이다. 이때 Latency를 선택한다는 것은 지연시간을 줄이는것을 선택하는 것이다. 

 


 프로젝트를 진행하며 MSA를 공부하고 있다. 내가 담당한 부분 중 하나는 인스타그램의 스토리와 유사한 기능이다. 

 알림과 유사한듯 유사하지 않은 스토리 기능은 일관성이 크게 중요하지 않은 기능이다 ! 

 

 

참고 

 

https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore?hl=ko#what-is-eventual-consistency 

 

Datastore로 strong consistency와 eventual consistency 간에 균형 유지  |  Cloud Datastore 문서  |  Google Cloud

Google Cloud Platform을 사용하면 Google의 확장 가능한 인프라에서 애플리케이션과 웹사이트를 빌드 및 호스팅할 수 있으며, 데이터를 저장 및 분석할 수 있습니다.

cloud.google.com

https://www.oracle.com/kr/database/what-is-a-relational-database/

 

관계형 데이터베이스란?

관계형 데이터베이스의 정의와 이를 비즈니스에 이용하는 방법을 알아봅니다.

www.oracle.com

https://velog.io/@soongjamm/Eventual-Consistency-%EB%9E%80

 

Eventual Consistency 란?

분산 시스템을 구성하려면 CAP 이론에 의해서 일관성과 가용성 중 하나를 포기해야하는 상황이 올 수 있습니다.클라이언트의 요청을 받았을 때, A서버의 데이터가 변경되면 즉시 다른 서버에 반

velog.io

https://www.youtube.com/watch?v=hUd_9FENShA

 

'CS > DB' 카테고리의 다른 글

SQL Injection이란?  (5) 2022.10.03
DB 데드락이란?  (3) 2022.08.30
DB Index 란  (7) 2022.08.15

사진은 자주 찍으면서 왜 일기는 작성하지 않을까? - 기획

 어떤 블로그에서 봤던 글귀인데 다시 못찾겠다..

 이미지를 통해 시각적으로 제공하는 정보가 텍스트에 비해 훨씬 많고 편리하다. 누구나 들고 다니는 핸드폰으로 터치 한 번에 고화질 사진이 찍히는 시대에서 시간을 내고 일기를 작성한다는 것은 어떻게 보면 시간 낭비라고 생각할 수도 있다. 

 

 하지만 일기는 당시의 내 생각을 구체적으로 적을 수 있다. 동시에 글쓰기 능력이 향상되고, 표현능력을 향상시킬 수 있다. 그리고, 아무에게도 말못할 사정과 심정을 표현하며 스트레스를 해소할 수 있다. (내 군대에서의 일기를 보면 간부, 선임 욕이 반이다.) 아무튼 그날의 감정과 생각을 구체적으로 회상할 수 있는 일기를 사람들이 왜 안쓸까? 책상 앞에서 작성을 하던가, 폰으로 작성을 하던가 양손이 묶여있어야 한다. 책상앞에서 종이에 작성한다고 하면 침대에서 거기까지 가기 얼마나 힘든일인지.. 또한 시간을 많이 소비해야하며, 일기와 같이 경험했던걸 다시 정리하기보단 유튜브나 넷플릭스 같이 새로운 컨텐츠를 경험하는것이 자극적이고 즉각적인 보상과 재미가 있다.

 

 침대에 누워서, 혹은 시간에 구애받지 않고 편리하게 일기를 재미있게 작성할 수 있는 방법을 생각해봤다. 

 바로 음성녹음이다. 핸드폰이 내 목소리가 들리는 위치에 있다면 작성할 수 있다. 침대에 누워서도, 변기에 앉아서도, 샤워하면서도 작성할 수 있다. 그럼 음성녹음으로 일기를 작성할 때 발생하는 다른 불편한 점은 없을까? 그 날의 내용을 한 눈에 파악하기 힘들며 그날 기분이 어땠는지 음성을 들어봐야 알 수 있다는 단점이 있었다. 글로 작성된 경우 해당 내용을 문단별로 넘어가며 파악하기 쉬우나 음성은 넘겨 듣는다해도 비교적 파악하기 힘들다.

 

 이러한 단점들을 해결하기 위해 노력했다. 이 회고글은 그 방법들에대한 내용, 아쉬운 점, 좋았던 점, github에 코드로 작성되지 않은 부분을 위주로 정리하는 글이다. 

 


달력을 통한 그날의 대표 감정 표시 

ㅎㅎ 캐릭터도 팀원분이 직접 그렸다.

 달력을 통해 녹음된 일기에서 그날의 대표 감정을 분류하여 한 눈에 보여주고자 했다. 

 이를 통해 어떤 감정으로 이 달을 살았는지, 굳이 음성을 다 들어보지 않아도 어떤 감정이었는지 사용자에게 보여주고자 했다. 물론 말은 쉬웠으나 기술적으로 많은 문제가 있었다. 

 

 STT(Google api 활용)를 활용하여 음성기반으로 작성된 일기를 텍스트기반으로 작성된 일기와 동일하게 보여주고자 했다. 

 이를 통해 꼭 음성일기를 꼭 들어봐야한다는 단점을 해소하고 그 날의 일기 내용을 조금 더 수월하게 파악할 수 있도록 했다. Google api를 활용한건 사실 무엇보다 50건정도가 무료라는 점이 가장 컸고 외국기업임에도 불구하고 높은 정확도를 보여주었다.

 

 

감정 분류 - AI 

감정을 분류하는데 중요한 정보는 발화 내용과 목소리라고 생각했다. 

예를 들어 "미친거 아니야??" 라는 글이 있다고 했을 때 "ㅋㅋㅋ미친거 아니야?" "와..미친거 아니야?" 는 다른 감정을 표현하고 있고 

이걸 분류하기 위해서는 내용뿐만아닌 목소리에서 나오는 정보가 중요하다고 생각했다. 

 

 따라서 Text기반 감정분류 모델, 음성기반 감정분류 모델을 학습시킨 뒤, 각 모델의 결과를 앙상블하여 사용하고자 했다. 

 이때 Text기반 감정분류는 위에서 말한 STT를 통해 만들어진 Text를 기반으로 감정을 분류했다. 음성기반 감정분류는 팀원분께서 직접 모델을 설계하고 학습시켜 활용했다. 약 5주 정도의 기간에 f1 score기준 약 0.62를 보여주었다. 인공지능을 처음 접하신 분이였음에도 불구하고 짧은 시간동안 잘해주셨다. Text는 KoBERT를 활용했는데 f1 score로 약 0.81를 보여주었다. 약 2주 동안 진행했는데 NLP모델은 처음 경험해봐서 모델을 공부해가며 직접 설계하고 학습시키기에는 내 할 일이 너무 많았다. 따라서 데이터 위주로 변경하여 정확도를 향상시키고자 했다. 

 

 데이터셋은 이곳저곳에서 많은 데이터를 가져왔다. 그리고 pandas의 stratified split을 통해 test data set을 나누어 따로 저장했다.

 데이터의 불균형은 예상된 문제였고 이를 해결하기위해 augmentation 기법을 찾아봤다. Image의 경우 상하좌우 반전하고 잘라붙이고 섞고 노이즈 넣고 난리부르스를 쳤었는데 Text는 어떻게 해야할지 감이 안잡혔다. 여러 방법을 찾아봤는데 주어 동사 앞뒤를 바꾼다던가, 단어의 위치를 치환한다던가 여러 방법이 있었다... 

 

감정의 애매함

 일반적인 감정분류 데이터셋을 보면 공포, 혐오, 놀람, 분노, 중립, 행복, 슬픔 으로 7가지 클래스가 존재했다. 

 근데 감정이란게 너무 애매했다. 예를 들어 "직장 상사가 일을 끝도 없이 줘" 라는 데이터에 분노라고 라벨링되었으나, 내가 저런 말을 할때면 슬픔을 느꼈을것이다. 이런 애매함을 해결하기 위해 음성 분류 모델을 추가한거지만 일단 학습자체가 잘 안됐다. 직장 상사라는 말만 나오면 죄다 분노로 분류했다. 예를 들어 "직장 상사랑 밥먹었어." 라는 문장도 분노로 표시했고, 당시 test data set에 대한 f1 score는 0.6~7 정도였다. 결과에 비해 높은 f1 score를 보여주었다고 생각됐고, 데이터 자체가 애매하다는 생각이 커졌다. 

 

감정을 줄이기로 했다.

 예를 들어 공포와 놀람, 혐오와 분노는 어느정도 비슷한 감정이라 생각했다. 

 "엘레베이터에 갇혔어" 라는 문장은 공포라고 라벨링 되어있었으나 놀람이라해도 무방했다. 또한 "룸메이트가 술만 마시면 맨날 바닥에 토해" 라는 문장은 혐오라고 라벨링 되어있었으나 우리 팀원 모두가 분노라고 생각했다. 공포와 혐오에 대한 데이터셋을 100개씩 추출해서 읽어보았을때 십중팔구 합쳐질 수 있는 감정이었다. 심지어 분노, 놀람 등은 다른 감정들에 비해 적은 수의 데이터가 있었고 이를 합친다면 데이터셋도 비교적 균형잡힌 데이터로 바뀌었다.

 다른 팀원들과 상의 끝에 감정의 갯수를 줄이기로 결정했고 최종적으로 놀람, 분노, 중립, 행복, 슬픔 다섯가지 감정으로 나누었다. 그 결과 새롭게 만든 test data set에 대해 f1 score는 0.81을 보여주었다. test data set도 변경되고 class도 줄어들어 정확한 정확도 비교는 아니였지만, 눈에 띄는 정확도 향상과 실제 새로운 텍스트를 입력했을때 보여주는 결과는 아래와 같았고, 만족스러웠다. 

 


 

DevOps 환경 구축

 7주 정도의 기간동안 진행된 프로젝트로 완성도 높은 서비스를 만들려면 빠른 기획과 빠른 개발이 필수적이었다. 

 빠른 기획과 빠른 개발은 아무리 꼼꼼하게 진행하려 노력하더라도 빈틈이 보이기 마련이고, 그때마다 다시 수정하고, 개발, 배포하는 과정이 필요했다. 따라서 폭포수보다는 애자일방식을 필요로 했다. API명세서는 초기에 꼼꼼하게 작성한다고 했지만 일정부분 변경이 이뤄졌고, 추가된 기능, 삭제된 기능도 있었다. 매일 스크럼을 진행하고 수정사항과 개선사항, 문제점등을 공유했다. 프론트엔드와 백엔드, 인공지능을 담당한 팀원들이 서로 협업하며 개발, 배포를 반복했는데 이 과정에서 프론트엔드는 빠르게 배포된 백엔드 서버에 테스트하며 개발을 진행했고, 백엔드는 프론트엔드와 소통하며 response를 수정해나가고, 분류 모델은 정확도가 올라가면 해당 모델로 변경했다. 

 

 Jenkins와 Gitlab을 연동하여 Develop 브랜치의 내용이 변경될때마다 자동으로 빌드하고 Docker를 활용하여 배포했다. 해당 develop 브랜치는 개발서버로 개발속도가 느리던 프론트엔드가 실제 서비스 환경과 같은 백엔드 서버에 직접 요청하며 테스트할 수 있도록 하기 위함이었고, 프론트엔드의 특정 부분이 완성되기까지 백엔드 서버가 미처 발견하지 못했던 에러나, 수정사항을 찾고 반영하며 업데이트가 계속 이뤄졌다. 그리고 특정 기능이 완성되면 release 브랜치에 머지하여 빌드하고 배포했다.(를 계획했으나 결국 마지막 한 번의 release 브랜치 배포로 끝났다 ㅎㅎ;; ) Spring 서버의 경우 1~2분으로 짧은 다운타임이 있었으나,  FastAPI서버의 경우 음성모델은 TF, 텍스트모델은 Torch로 구현되어 두개의 라이브러리를 설치하는데 약 13분의 긴 다운타임이 있었고, 해당 다운타임은 일기를 작성중이던 사용자들에게 소중한 일기 한 편 한 편이 날아가는 치명적인 문제점이었다. 따라서 Blue-Green 방식의 무중단 배포환경을 구축하여 서비스의 다운타임을 해결하였다. 

 

모델 가중치 파일의 흐름... 이거보다 자세하게 그릴수가 없는데 팀원은 왜 뭐라하지..

 인공지능 모델의 경우 처음 생각한 방법은 MLFlow를 도입하여 실험을 추적관리하고 API를 통해 활용하고자 했다. 

 하지만 MLFlow를 도입하고 사용해보는 것은 순전히 내 욕심이었다. 팀원의 진행사항이나 문제점을 계속 듣고 살펴본 결과, MLflow까지 적용하면 벅찰듯했다. 무엇보다 나도 정확하게 아는것이 아니며 사용해본 경험이 없었으므로 사용하자는 말을 꺼내기 망설여졌다. 그 결과 나는 TensorBoard나 WandB를 팀원에게 추천해주었고, TensorBoard를 통해 실험내용을 공유했다. 그리고 결국에는 학습된 모델을 활용하기 위해 gpu서버에서 학습하여 저장된 가중치 파일을 구글 클라우드에 올린 뒤 EC2 혹은 로컬에서 다운받아 사용했다.

 

 


 

 아쉬운 점 ;-; 

일단 가장 아쉬운 점은 하나를 깊게 가져가지 못했다는 점이다. 정신차려보니 BE, Infra, AI를 담당했는데 그 결과 인공지능의 경우 기존에 알던 내용을 기반을 위주로 진행했다. 즉 별도로 깊게 공부하지 않고 NLP에 손을 댔다.. 

 

 내 멋대로 Ensemble..

 일기는 여러개의 문장으로 구성되어있다.  문장이 쌓이고 쌓일수록 긴 글이되고 이는 NLP모델이 글을 해석하기 힘들어진다고 알고있다. 이를 해결하기 위해 LSTM, Transformer 등 다양한 기법이 나왔는데 KoBERT는 Transformer 기반 모델이다. 아무튼 여기서 조금 아쉬운 점은 별도의 실험 없이 느낌으로... 전체적인 글을 한 번에 예측하기보다 문장별로 나누어 분류하고 합치는게 더 정확도가 높지 않을까 싶어서, 나는 문장별로 감정을 분류한 다음 가장 많이 나온 감정을 대표 감정으로 설정했다. 여기서 추가된 api가 감정 변화 그래프이다. 대표감정만을 그래프로 보여주기보다 더 많은 정보를 보여주기 위해 각 문장별 감정을 세고, 그래프로 보여주었다. 그리고 대표 감정을 분류할때 음성기반의 감정 분류 결과를 Weight Voting Ensemble을 진행했다. 이때 음성에 대한 weight를 고정값으로 주었는데 이는 음성과 텍스트 분류의 정확도를 비교하여 단순 감으로 때려잡은 값이었다...ㅎㅎㅎ;; 이 값을 차라리 전체 문장의 개수와 길이, 각 모델의 confidence까지 고려하여 Soft voting으로 했다면 더 좋은 정확도를 보여줄 수 있었다고 생각한다. 

 

 MLOps를 제대로 경험하지 못한 점은 여전히 아쉽다. 

 인공지능 모델의 경우 수동으로 서버에 저장했다. 프로젝트의 볼륨이 작아 실험이 많지 않았기에 불편하진 않았지만 나중에 실험하는 양도 많아지고 볼륨이 커지면 분명 번거로운 작업이라 생각된다. 이를 자동화하고 싶었는데 그러지 못한 점이 아쉽다. 그래도 무중단 배포 환경은 정말 만족할만한 경험이었다. 또한 사용자로부터 감정이 수정되었을때 해당 데이터를 저장하여 나중에 추가로 학습한 뒤 모델의 성능을 향상시키고자 했는데 이또한 적용하지 못했다.. 가장 아쉬운 점이다. 

 

 Backend 역할에서 새롭게 배우고 적용해본것이 적다. 

 거의 없다고 봐도 무방하다.. Java에서는 stream을 공부하고 구분하여 적용하였는데,,,, 아직도 언제 뭘 써야지! 라는 확신은 없다. 그리고 이전에는 동작원리도 자세히 모르고 사용했던 JPA에 대해 공부했다. 공부하고 보니 이전 프로젝트에서는 정말 개판으로 사용했다는걸 알았다. ;-;..  FastAPI에서는 인공지능 모델을 싱글톤으로 사용했다. 서버가 재실행됐을때 바로 모델을 불러온 뒤 이후에는 계속 해당 객체를 불러와 사용하였다. 이때 여러 요청을 보내도 이미 할당된 객체를 불러왔다 !  이외에는 뭐.. 딱히 새롭게 배우고 적용해봤다 ! 개선했다 ! 라고 할만한 경험이 없어 아쉬웠다. 

 

 


 

부캠의 회고와 싸피의 회고

 

 부캠 AI Tech에서는 대회가 끝날때마다 회고글을 제출해야해서 작성한 경험이 있다.

 형식도 정해져있지 않았고 순전히 내 멋대로 작성해서 제출해야했다. 내가 프로젝트를 하며 생각났던 짤이나 문구를 붙여넣으며 회상하기 쉽도록 작성했다. 

내 첫 회고글.

 아직도 저 EDA,,, 그게뭔데,, 하던 감정이 생생하다. 당시 팀원들이 pd, plt, seaborn 뭐 다 잘쓰고 그러니까 위축돼서 팀원들의 코드를 참고하며 열심히 데이터를 파악하려 노력했다. 아무튼 저런 회고글을 올리며 부족한점, 아쉬웠던 점들을 정리하고 기록하며 다음 대회, 프로젝트에서는 발판삼아 빠르게 발전할 수 있었다. 

 

 싸피에서는 회고를 강제하지 않는다. 때문에 많은 교육생이 프로젝트를 회고하지 않는듯하다. 

 블로그 포스팅을 자주하는 팀원이 아니였으면 나도 싸피가 끝날때까지 회고를 미뤘을거 같다..

 

 싸피와 부캠 AI Tech를 비교했을때 각자 장점이 있지만 회고, 공유 문화는 부캠이 압도적으로 좋은듯 하다. 싸피의 경우 프로젝트를 엄청나게 잘만들어도 프로젝트 발표를 못하면 그만이라는 생각이 크다. 때문에 발표에 있어 사용된 기술을 공유하고 소개하며 아쉬운 점, 어려운점을 공유하기엔 청자의 관심을 끌기 힘들며 발표에서 좋은 점수를 받기 힘들다. 너무 다양한 프로젝트가 존재하고, 분야가 다른걸 개발하는데서 오는 한계라고 생각된다. 반대로 부캠의 경우 CV, NLP를 나누어 대회를 진행하고, 각 대회에서 모든 팀원들을 섞어 기술이나 문제점을 공유하고, 성능이 오른 방법을 공유하며 모두가 함께 최고 점수의 벽을 뚫기 위해 노력했다. 

 

 실제 프로젝트를 소개하고 발표하는 자리라면, 싸피만큼 좋은 문화가 또 없다. 부캠이 기술에 집중했다면, 싸피의 경우 프로젝트에 집중했다고 생각한다. 기술 말고도 발표 자체에 대한 피드백도 받을 수 있다. 개발외적으로도 신경을 많이 써주는게 싸피라고 생각된다. (돈도 많이 주고 밥도 맛있음.)

 

 

 

프로젝트 repo

 

GitHub - JODONG2/VODA

Contribute to JODONG2/VODA development by creating an account on GitHub.

github.com

 

JPA (Java Persistence API)

JPA는 JAVA의 객체지향과 관계형 DB를 매핑해주는 ORM(Object Relational Mapping) 기술이다.

 

장점으로는 

1. 특정 데이터베이스에 종속되지 않는다.

2. 객체지향적이다.

 

단점으로는

1. 복잡한 쿼리는 처리하기 힘들다.

2. 자동으로 생성되는 쿼리로 인해 개발자가 의도하지 않은 쿼리를 날리면서 성능저하가 있을 수 있다. 

3. 쓰긴 쉬운데 잘쓰긴 어렵다.

++ 특정 데이터베이스에 종속되지 않는다.

#Oracle의 현재 시간 
SYSDATE

#MySQL의 현재 시간
NOW()

위처럼 DB마다 쿼리가 조금씩 다르다. 하지만 JPA는 추상화된 데이터 접근 계층을 제공하고 이를통해 같은 함수라면 같은 결과를 기대할 수 있다. 

 

++ 객체지향적이다. 

@Entity
@Table(name = "A")
public class A{
	. . . 
    @NToM(~~)
    @JoinColumn(name="b_id")
    private B b;
}

테이블 하나와 매핑되는 클래스가 뚝딱 만들어지는데 이는 몹시 객체지향적이다. 내부에서 다양한 annotation을 활용하여 연관관계(N:M, 1:N, N:1, 1:1)를 매핑할 수 있다.  


Entity

JPA Entity Manager

Entity

entity란 DB의 테이블에 대응하는 클래스이다. 

@Entity annotation을 통해 Entity Manager가 관리한다.

 

Persistence Context (영속성 컨텍스트)

entity를 영구 저장하도록 지원하는 환경으로 Entity manager를 통해 접근한다. 

Application과 DB 중간사이의 계층의 영속성 컨텍스트는 버퍼링과 캐싱 등에서 이점을 갖는다. 

Entity Manager

Persistence Context 에 접근하여 DB작업을 제공하는 객체이다.

따라서 Entity Manager를 통해 DB Connection을 이용하고 DB에 접근한다.

Transaction단위를 수행할때마다 생성된다. 즉 사용자의 요청이 올때마다 생성되고 끝나면 닫는다.

Transaction 수행후에는 반드시 Entity Manager를 닫으면 내부적으로 DB Connection을 반환한다.

thread간 공유하지 않는다. 

Entity Manager Factory 

entity manager instance를 관리하는 주체이다.

Application 실행 시 한개의 Entity Manager Factory가 생성된다.

사용자로부터 요청이 오면 Entity manager를 생성한다. 

 


Entity Lifecycle 


new : 비영속 상태로 new 키워드를 통해 생성된 상태이다. 아직 영속성 컨텍스트에 저장되지 않았다.

Managed : 영속 상태로 엔티티가 영속성 컨텍스트에 저장되어 관리되는 상태이다. Application과 DB사이 계층에 존재하여 DB의 값과 차이가 있을 수 있다. 트랜잭션 commit 후 DB와 값을 일치시킨다.

Detached : 준영속 상태로 엔티티가 저장되었다가 분리된 상태이다. commit 전에 detached됐다면 엔티티가 변경되어도 쿼리가 날아가지 않는다. 

Removed : 삭제 상태로 영속성 컨텍스트에서 삭제되고 flush()혹은 commit()이 실행되면 DB에서도 삭제된다. 

 

 

 

 

* commit()과 flush()

flush는 jpa의 영속성 컨텍스트와 DB를 동기화하는 작업을 말한다. flush()가 실행되면 DB에 쿼리를 날린다.

이 때 변경감지(Dirty Checking)를 통해 수정된 entity를 update하는 쿼리를 날린다. 또한 *쓰기 지연된 쿼리들을 한 번에 보낸다. 

 

commit은 트랜잭션을 begin()으로 실행한 뒤 commit()하여 트랜잭션을 종료한다. 이때 commit()은 내부적으로 flush()를 호출하며 트랜잭션을 끝내는 역할을 한다. 

 

가장 큰 차이는 flush를 통해 전송된 쿼리는 rollback될 수 있지만 commit은 트랜잭션을 종료하므로 rollback될 수 없다. 

 


Persistence Context 이점

Persistence Context는 Application과 DB사이의 계층에서 여러가지 작업을 수행할 수 있다. 

1차 캐시

영속성 컨텍스트 내에서 저장되는 캐시로 Map<Key, Value>로 저장된다.

entityManager.find() 메소드 호출 시 1차 캐시에서 조회하고, 없으면 DB에서 조회 후 1차 캐시에 저장하고 반환한다.

즉, 처음 find 호출 시 1차 캐시에 존재하지 않는다면 SELECT query가 날아가고, 다음 같은 key값을 find할 경우 SELECT query는 날아가지 않는다.

이를 통해 DB에 접근하는 횟수를 줄이며 성능을 향상시킬 수 있다. 

 

동일성 보장

하나의 트랜잭션에서 같은 Key값은 같은 Entity 조회를 보장받을 수 있다. 즉, == 연산에서 결과값이 true이다. 

반대로 MyBatis는 조회 결과를 다시 인스턴스화하여 return하기때문에 == 연산에서 결과값이 false이다. 

 

변경감지 (Dirty Checking)

1차 캐시에 DB에서 처음 불러온 entity의 스냅샷을 저장한다. 

이후 스냅샷과 비교하여 다른점이 있다면 UPDATE쿼리를 사용한다. 

find 메소드를 호출하여 entity를 불러오고, 값을 변경한 뒤 save메소드를 호출하면 insert가 아닌 update 쿼리가 날아가도록 해준다. 

반대로 entity의 ID와 1차 캐시에 같은 ID를 찾을 수 없다면 insert 쿼리가 날아가도록 해준다. 

*쓰기 지연

영속성 컨텍스트는 트랜잭션 처리를 도와주는 쓰기지연을 지원한다.

 한 트랜잭션안에서 이뤄지는 UPDATE나 SAVE의 쿼리는 쓰기지연 저장소에 저장되었다가 트랜잭션이 commit(내부적으로는 flush)되는 순간 DB에 날린다. 이를통해 DB Connection 시간을 줄일 수 있고 트랜잭션이 테이블에 접근하는 시간을 줄여 교착상태 등을 방지할 수 있다. 

 

** 하지만 특정 INSERT 쿼리는 즉시 날아간다. 

Entity가 영속(Managed)상태가 되려면 식별자(PK)가 필요하다. 이때 Entity의 ID 생성전략을  IDENTITY로 사용한다면, 이는 데이터베이스에 실제로 저장한 뒤 다른 식별자들과 구분한다. 따라서 Insert쿼리는 즉시 날아가고 이런 경우에는 쓰기지연을 통한 성능 최적화를 얻기 힘들다. 

생성전략을 Sequence로 사용한다면, entityManager가 entity를 영속화하기전에 DB Sequence를 먼저 조회하고 조회한 식별자를 통해 Entity를 생성한뒤 영속화한다. 그 후 flush를 통해 Entity를 DB에 저장한다.

 

 


이전에 JPA를 사용해보기전에도 공부를 목적으로 포스팅을 한 번 했었다. 지금 그 글을 보니 너무 뜬구름 잡는 내용이고 개념도 정확하게 작성되지 않은 게시글을 포스팅했다. 지금은 삭제했다. 수정하는거보다 이렇게 새로 적는게 빠를 정도였으니까 :-)

+ Recent posts