-
도메인 로직이 항상 좋을까?
프로젝트를 하면서 JPA를 활용한 도메인 로직이 항상 좋을까? 에 대해 고민했던 내용인데요 간략하게 정리해보겠습니다. **코드에는 정답이 없다고 생각하기 때문에 하나의 문제 해결 관점이라고 봐주시면 감사하겠습니다. - 도메인 로직(JPA) 으로 좋아요를 구현했을 경우 - 개선 및 도메인 로직이 안좋을때 - Conclusion 1. 좋아요 눌렀는지를 JPA조인으로 가져올 경우 도메인 로직을 활용해 (게시글에) 좋아요를 추가/삭제 하기 위해서는 아래와 같은 로직이 필요합니다. 좋아요를 한 번도 안 눌렀다면 좋아요를 추가하고, 이미 눌렀다면 눌렀던 좋아요가 취소되는 로직입니다. 그런데 여기서 문제가 발생합니다. contains( ) 메서드를 실행하기 위해서는 내부에 어떤 값이 있는지 알아야 하기 때문에 JPA..
2022.09.19
-
JPA를 사용하며 느낀 단점
프로젝트에서 JPA를 사용하며 느꼈던 단점에 대해 작성해보겠습니다. 크게 데이터베이스와 반대되는 사상, 복잡성과 추가쿼리, 자동화, JPA에 의존하는 정도의 단점이 있었는데요, 이를 하나씩 살펴보겠습니다. ** 기술을 사용하며 느꼈던 점이기 때문에 저런 시각으로 볼 수도 있구나 정도로 이해해주시면 감사하겠습니다. 결론만 보고 싶다면 제일 끝으로 이동하시면 됩니다. - 데이터베이스와 반대되는 사상과 러닝커브 - 복잡성 - 자동화 - JPA의 기술에 의존 (feat. 도메인 모델이 엔티티가 되는 문제점) 1. 데이터베이스와 사상의 차이, 러닝커브 JPA가 높은 러닝커브를 가지는 가장 큰 이유는 관계형 데이터베이스와 객체지향적사상의 지향점이 다르기 때문입니다. 관계형 데이터베이스는 "데이터를 중복없이, 효율적..
2022.08.12
-
JPA를 사용하며 느낀 장점
프로젝트에서 JPA를 사용하며 느꼈던 장점에 대해 작성해보겠습니다. 크게 영속성, 자동화, 객체지향적인사고, 컴파일 시점에 에러를 알 수있다 정도의 장점이 있었는데요, 이를 하나씩 살펴보겠습니다. ** 기술을 사용하며 느꼈던 점이기 때문에 저런 시각으로 볼 수도 있구나 정도로 이해해주시면 감사하겠습니다. 결론만 보고 싶다면 제일 끝으로 이동하시면 됩니다. - 영속성 - 자동화 - 객체지향적인 사고 - 컴파일 시점의 오류 1. 영속성 JPA 이점 중 하나는 영속성을 활용할 수 있다는 점입니다. 영속성은 우리 눈에는 보이지 않지만 레이어 사이에 존재하는 중간 계층인데요, 중간 계층이 생기게 되면 캐싱을 하거나 지연로딩과 같은 다양한 최적화를 사용할 수 있습니다. 영속성 덕분에 한 번 조회된 데이터는 같은 트..
2022.08.11
-
[11월] 1주차
내가 만들고 싶었던 웹 사이트가 어느정도 끝이났다. 더 이상 개발은 안할 예정이고 하루 1-2시간 정도씩 코드만 손 볼 예정이다. 생각보다 프로젝트가 길었는데 재미있었고 그만큼 배운게 너무나도 많아 정말 만족스러웠다. 이론을 학습하는 것도 중요한데 프로젝트를 통해 학습하는 것의 중요성도 정말 많이 깨달았다. 요즘 호눅스가 코코아때 해주셨던 말들이 많이 떠오르는데 음... 그때는 이해가 안되던게 조금씩 이해가 되고 있다. 앞으론 내가 구현한 내용에 대한 정리와 코테, CS에 몰빵해서 만족할 만한 결과를 얻어야겠다. 사이트가 어느정도 돌아가기까지 테스트해주고 도와주신 분들께 진심으로 감사할 따름이다. 1. 학습내용 - 쿠키/세션 정리 쿠키와 세션에 대해 정확한 이해가 없었던 듯 했다. 병철이와 창훈이가 질문..
2022.11.04
-
값을 검증하는 위치는 어디가 좋을까?
프로젝트를 진행하며 값 검증 위치에 대해 고민했던 내용인데요, DTO에서 @Validation 을 사용해 값을 검증하는 방법을 바꾸고자 했던 내용입니다. 정답은 없기 때문에 하나의 의견으로 읽어주시면 감사하겠습니다. JPA를 사용했기 때문에 글을 읽기 위해서는 JPA에 대한 기본적인 이해가 필요합니다. 1. DTO에서 값 검증 엔티티를 생성하기 위해 값을 DTO에서 검증할 수 있습니다. 이는 아래와 같은데요, 클라이언트로부터 받은 값을 어노테이션을 통해 검증하는 것입니다. 컨트롤러에서 @Valid 어노테이션을 사용해야 하지만 어노테이션을 통해 값을 간편하게 검증할 수 있습니다. 하지만 아래와 같은 몇가지 의문점이 들었습니다. 였습니다. 요구사항이 바뀌면 DTO를 재활용할 수도 없으며 매번 조건을 변경해줘..
2022.07.30
-
커버링 인덱스는 대체 얼마나 빠를까?
프로젝트를 배포하기 전 마지막으로 점검하며 커버링 인덱스의 실제 성능에 대해 테스트했던 내용입니다. 학습 과정에서 작성되었기 때문에 잘못된 내용이 있을 수 있습니다. 1. 인덱스가 없을때 약 1억건의 데이터에서 인덱스가 걸려있지 않은 회원 아이디로 검색을 해보겠습니다. 모든 회원 아이디는 Auto-Increment로 중복되지 않습니다. 중간 조금 아래 있는 데이터를 검색했는데요, 약 1분 23초 정도가 걸렸습니다. 2. 인덱스가 있을때 커버링 인덱스로 검색했을때는 0.01초 밖에 걸리지 않는 것을 볼 수 있습니다. 몇 배라고 표현할 수도 없는 정도인데요, 인덱스가 항상 좋은 것은 아니지만 기본적으로 데이터가 많아졌을때 반드시 필요한 요소라는 것을 알 수 있습니다. 물론 인덱스를 사용하는게 무조건 좋지는 ..
2022.09.29
-
[10월] 3주차 회고
이번주는 병철이를 만나 CICD를 배웠다. 인프라, CICD가 나한테 가장 걸림돌이었는데 이렇게 하나씩 단계별로 설명을 듣고나니 또 생각보다 재밌고 더 잘하고 싶었다. 내가 부족했던 부분이 조금씩 채워지고 있어서 개발이 재미있어지는데 조금만 더.. 조금만 더 힘내자. 사실 요즘 너무 힘이 빠져서 무기력한데 잘 이겨내겠지...... 사실 무기력감의 가장 큰 원인은 우리 사이트인데 만들면 자꾸 프론트에서 버그가 터져 정말 정말 하기 싫었다. 변경된 것도 없는데 멀쩡하던 코드가 자꾸 터지니까 속에 열불이 터졌고 목구멍까지 화가 올라오는걸 꾸역꾸역 견뎠다. 후... 이것도 지나가리라... (물론 프론트 잘못은 아닌거 아는데 진짜... 진짜 힘들었다) 1. 학습내용 - 자동배포 - 테스트코드 작성 자동배포에 대해..
2022.10.21
-
NGINX를 사용하는 이유
NGINX를 학습과정에서 작성된 글이기 때문에 잘못 이해하고 정리된 내용이 있을 수 있습니다. 내용은 피케이님의 우아한 테크코스 영상을 참조/정리 했으며, 내용이 정말 좋기 때문에 NGINX를 사용하신다면 꼭 참조해보실 것을 추천드립니다. 1. NGINX의 탄생과정 APACHE와 엔진엑스가 태어났을 때의 웹 상황을 이해하면 왜 NGINX를 사용하는지 더 잘 이해할 수 있는데요, 이를 살펴보겠습니다. 1995년에는 NCSA HTTPd가 존재했는데 이는 버그가 많아서 개발자들이 많은 불편함을 겪었습니다. 이 과정에서 구조를 변경하고 기능을 추가해 만든 것이 APACHE입니다. 그런데 아파치서버의 구조는 요청이 들어오면 Connection을 위해 프로세스를 매번 만듭니다. 이는 UNIX 계열 OS가 네트워크 ..
2022.10.27
-
[10월] 1주차 회고
드디어 서비스가 출시됐다. 앞단에서 버그가 좀 많아서 아직 해결해야할게 많지만 뿌듯했다. 물론 서버쪽도 조금 아쉬운게 있어서 이것저것 리팩토링 했는데 그 과정에서 성능 최적화나 내가 보지 못했던 부분에서 꽤 많은 부분을 수정할 수 있었다. 오랜기간 해왔던 프로젝트가 결실을 맺어서 굉장히 기쁘고 이제는 코테나 CS 등 내가 부족한 부분에 집중해야겠다. 사람들이 시간내서 이것저것 테스트를 해줬는데 너무 고마웠다. 1. 학습내용 - 오픽 - 자바 기초문법 - 코테 이번주는 오픽 학원에 등록했다. 만료된 자격증을 갱신한다는 핑계로 학원을 등록했는데 잊었던 영어 말하기를 오랜만에 해서 굉장히 재밌었다. 사람들과 금방 친해져서 이런저런 이야기를 했는데 영어로 말하다보니 학부때 열심히 영어 공부 하던게 생각나고 좋았..
2022.10.07
-
팔로잉/팔로워 구현에서 마주친 동시성
프로젝트를 진행하며 팔로워 수를 증가하거나 줄일때 동시성 문제에 대해 고민했던 내용입니다. 학습과정에서 작성되었기 때문에 잘못된 내용이 있을 수 있습니다. 1. 동시성 문제 현재 팔로우/언팔로우 기능을 구현하는 로직은 아래와 같습니다. 팔로잉/언팔로우을 하게되면 회원 테이블이 가지고 있는 팔로잉 컬럼의 값이 1 증가하거나 감소하는 로직인데요, 이 부분에서 동시성 이슈가 발생했습니다. 여기서 왜 회원 테이블이 팔로잉 카운트를 가지고 있는지에 대해 의문이 생길 수 있습니다. 이는 조회의 편의성을 위해 이렇게 설계했습니다. 한 회원의 몇명의 팔로잉/팔로워 수를 알기 위해 서는 조인으로 데이터를 찾아와 갯수를 세거나 데이터베이스에 직접 카운트 쿼리를 날려야 하는데, 이렇게 매번 조인/카운트 쿼리를 날리는 것은 ..
2022.09.19
-
HTTPS 통신이 이루어지는 원리
프로젝트에서 HTTPS를 적용하기 위해 학습했던 내용에 대해 정리해보려고 합니다. 학습과정에서 작성되었기 때문에 잘못된 내용이 있을 수 있습니다. - HTTPS와 부가개념 - 동작원리 - 결론 / HTTPS를 적용했던 이유 1. HTTPS HTTP는 정보를 텍스트로 주고 받기 때문에 네트워크에서 전송 신호를 가로채는 경우 원하지 않는 데이터 유출이 발생할 수 있습니다. 이러한 보안 취약점을 해결하기 위해 등장한 프로토콜이 HTTPS 입니다. **와이어샤크를 통해 패킷을 복호화시키는 과정에 대해 해당 블로그에서 잘 설명해주셨습니다. HTTPS는 HTTP와 거의 동일하지만 데이터를 주고 받는 과정에 보안 요소가 추가됩니다. HTTPS를 사용하면 서버와 클라이언트 사이의 모든 통신 내용이 암호화되는데, 즉 페..
2022.08.28
-
스켈레톤 UI를 사용하며 느낀 점
프로젝트에서 스켈레톤 UI를 적용하며 느낀점에 대해 간략하게 작성해보겠습니다. 학습과정에서 작성됐기 때문에 잘못된 내용이 있다면 언제든 지적 부탁드립니다. - 스켈레톤 UI의 개념 - 백엔드 관점에서 바라본 스켈레톤 UI - Conclusion 1. 스켈레톤 UI란? 실제 데이터가 렌더링 되기 전 보이게 될 화면의 윤곽을 먼저 그려주는 UI 기법입니다. 아래와 같이 데이터가 전송되지 않았는데 어떻게 배치될지에 대한 그림을 본 적이 있을텐데요, 즉 사용자에게 어떤 내용이 보여질 것인지에 대한 화면의 뼈대라고 할 수 있습니다. 로딩이 완료되면 윤곽에 데이터가 대체되어 화면이 부드럽게 전환되기 때문에 체감 로딩 시간이 짧다는 장점이 있지만, 화면마다 새로운 스켈레톤 UI를 적용해야 하기 때문에 시간과 비용이 ..
2022.10.10
-
private 생성자 올바르게 사용하기
프로젝트를 진행하며 private 생성자를 올바르게 사용하기 위해 이펙티브 자바 아이템4를 읽고 추가적인 내용을 보강했습니다. 학습 과정에서 작성되었기 때문에 잘못된 내용이 있을 수 있습니다. - 인스턴스화 방지(feat. 올바른 private 생성자 사용법) - 유틸 클래스와 객체지향 - Conclusion 1. 인스턴스화 방지 모든 클래스들이 인스턴스화가 필요한 것은 아니기 때문에 상황에 따라 private 생성자를 만드는데요, 이를 올바르게 사용하는 방법에 대해 알아보겠습니다. private 생성자를 사용하면 어느정도 인스턴스화를 방지할 수는 있지만 추상 클래스를 만들거나 리플렉션을 통해 인스턴스화를 할 수 있기 때문에 조심해야 합니다. 아래와 같이 추상클래스를 정의하고 유틸 메서드를 만들면 인스턴..
2022.07.17
-
엔티티에 equals & hashcode를 재정의 해야 하는 이유
엔티티를 중복체크할때 hashcode를 재정의하지 않고 contains( ) 메서드를 호출했던 내용인데요,컬렉션의 contains( ) 메서드에 대한 내용에 대해 간단히 공유해보겠습니다. ** 부끄럽지만 이 기본조차 잘 모르고 사용했습니다. 1. 컬렉션의 constains( ) 메서드 컬렉션의 구현체중 ArrayList의 메서드입니다. 내부 메서드를 타고가면 반복문을 돌면서 equals( ) 메서드로 객체를 비교하는데요, 즉 우리가 정의한 값들을 통해 해당 객체가 포함되어 있는지/없는지를 판단합니다. 두 번째는 HashSet의 constains 메서드인데요, 이는 ArrayList보다는 조금 복잡합니다. 우선 null이 아닐 경우 해시코드를 통해 해당 객체를 판단하고 다시 한 번 hashcode를 검사하..
2022.08.07
-
equals & hashcode 해시코드의 두 가지 정의 방법
equals & hashcode를 재정의해서 사용하는 방법은 크게 두 가지가 있습니다. 필드 값을 직접 비교하도록 하는 방법과 getter를 호출해 비교하는 방법입니다. 왼쪽은 필드 값을 직접 비교하는 방법이고 오른쪽은 getter를 호출해 값을 비교하는 방법입니다. 둘은 큰 차이가 없어보이지만 프록시를 사용해 동일성을 비교하는 경우 차이가 발생하게 됩니다. 프록시를 호출하는 순간 원본 엔티티를 조회해 값을 찾아와야 하는데 이때 getter를 통해 값을 호출하지 않으면, 즉 필드 값을 직접 비교하게 되면 null 값이 조회되기 때문입니다. 따라서 프록시를 사용하는 경우라면 값을 비교하기 위해 getter를 호출하는 순간 원본 엔티티의 값이 호출되도록 반드시 getter를 통해 equals & hashco..
2022.06.21
-
Replication 데이터베이스를 구성할때 주의할 점
프로젝트를 진행하며 Primary, Secondary 데이터베이스를 구성할때 주의할 점에 대해 정리한 내용입니다. 학습과정에서 작성되었기 때문에 잘못된 내용이 있을 수 있습니다. 1. 프로젝트 인프라 구성 현재 프로젝트의 구성은 아래와 같습니다. API 서버에서는 Primary, Secondary 데이터베이스를 바라보고 있으며, Primary 데이터베이스는 Secondary 데이터베이스를 바라보고 있습니다. 2. 문제상황 및 해결 그런데 Secondary 데이터베이스는 Primary 데이터베이스로 부터 쓰기 데이터가 연동되기 때문에 Primary 데이터베이스에서 오는 값을 받아야 합니다. 이를 위해서는 Secondary에서 Primary로부터 오는 포트를 개방해줘야 하는데요, 이를 개방해주지 않아서 문제..
2022.10.27
-
Auto-Increment와 UUID
좋아요와 이모티콘 기능을 구현하면서 PK에 대해 들었던 의문과 학습/생각정리를 위해 간략하게 글을 남겨봅니다. 1. Auto-Increment와 UUID. 그 의미 Auto-Increment의 경우 데이터가 추가될때 자동으로 PK값이 1 증가하며 UUID의 경우 VARCHAR 혹은 Binary 값으로 PK를 저장할 수 있습니다. Auto-Increment 같은 경우 순차적으로 값이 증가하기 때문에 값이 연속적으로 증가하는 것을 한 눈에 알아볼 수 있는 장점이 있는 반면 중간에 데이터가 유실되거나 하더라도 그 값이 채워지지 않습니다. 반면 UUID의 경우 랜덤한 값이 생성돼서 데이터베이스에 들어오기 때문에 연속적인 값들을 알 수 는 없지만 중간에 공백이 생기더라도 PK의 연속성을 신경쓰지 않아도 됩니다. ..
2022.09.13
-
@Component와 @Configuration의 차이
@Component와 @Comfiguration의 차이에 관해 정리해보겠습니다. 결론부터 말하면 @Component는 개발자가 작성한 클래스를 Bean으로 등록할 때 사용하고 @Configuration + @Bean은 개발자가 직접 제어하기 힘든 클래스를 빈으로 등록할때 사용합니다. **참조 종류 선언 사용 용도 @Component 클래스 사용자가 만든 클래스 @Bean 메서드 외부 라이브러리 1. @Component / @Configuration @Component가 달린 클래스는 자동으로 스프링 컴포넌트 스캔 대상이 되기 때문에 별도로 Bean을 등록하지 않아도 사용할 수 있습니다. 이는 스테레오 타입 어노테이션을 사용하는 클래스에 달려 있는데요, 스테레오 타입이란 우리가 흔히 사용하는 @Contro..
2022.07.24
-
[9월] 4주차 회고
드디어 오늘 서비스가 출시된다. 클라이언트에서 세부 작업할 내용이 조금 남아서 출시가 늦어졌는데 그래도 그만큼 깔끔하게 디자인이 돼서 어느정도 만족스럽다. 이제 회사들의 채용공고가 계속 뜨고 있는데 포트폴리오를 첨부해서 본격적인 지원을 시작해봐야겠다. 1. 학습내용 - 인프라 - 배포 - 이력서 작성 - 최적화 이번주는 인프라 세팅과 자동배포에 대해 학습했다. 지난주에도 올렸지만 아래와 같이 미리 환경을 세팅해뒀는데 여기에 자동 배포 파이프라인을 붙이고 싶었기 때문이다. 창훈이랑 같이 하다보니 나도 열심히 하게 됐고 정리는 정말 싫었지만 같이 하다보니 꼼꼼히 할 수 밖에 없게 됐다. 열심히 듣고 내가 약한 퍼즐을 다 채워야지! 혼자 했으면 절대 정리 안 했을 것 같은데 참 고마웠다. 여러가지 최적화를 해..
2022.09.30
-
트랜잭션과 잠금
1. MySQL 엔진의 잠금 MySQL에서 사용되는 잠금은 스토리지 엔진 레벨과 MySQL 레벨로 구성됩니다. MySQL 엔진은 스토리지 엔진을 제외한 나머지 부분으로 이해하면 되는데 이는 모든 스토리지 엔진에 영향을 미치지만 스토리지 엔진 레벨의 잠금은 스토리지 엔진 간 영향을 미치지 않습니다. MySQL 엔진에서는 테이블 데이터 동기화를 위한 테이블 락 이외에도 테이블의 구조를 잠그는 메타데이터 락, 그리고 사용자의 필요에 맞게 사용할 수 있는 네임드 락 기능도 제공합니다. 1-1. 글로벌 락 실행과 동시에 MySQL 서버에 존재하는 모든 테이블을 닫고 잠금을 겁니다. 다른 세션에서 SELECT를 제외한 대부분의 DDL 문장이나 DML 문장을 실행하는 경우 글로벌 락이 해제될 때까지 해당 문장이 대..
2022.05.24
-
[9월] 3주차 회고
고등학교 친구와 또 캠핑을 갔다. 본가에서 푹 쉬고 캠핑까지가서 다시 작업의지가 솟았는데 클라이언트가 아직도 안됐다고 했다. 조금씩 화가나기 시작했는데 그래도 차분히 기다려봐야겠다. 벌써 다됐을 프로젝트의 완성이 거의 한달째 지연 되고 있는데 이 일을 개기로 개발자의 기한지키기에 대해 깊게 생각해보게 됐다. 이런 기본적인 것들이 계속해서 안지켜지면 결국엔 근본적인 실력, 태도까지 의심받게 되는것 같은데 나도 이 부분에 있어 정말 조심해야겠다. 1. 학습내용 - 데이터베이스 최적화 이번주는 데이터베이스 최적화에 시간을 많이 쏟았다. RealMySQL을 공부하면서 구현했던 기능들을 크게 리팩토링했는데 좋아요/이모티콘에서 커버링 인덱스 태우기, 스켈레톤 UI 등 다양한 것들이 개선됐다. 아직은 데이터가 적어서..
2022.09.18
-
하나의 레포지토리로 하위 엔티티 관리하기
프로젝트를 진행하며 애그리게잇에 대해 학습하고 고민했던 내용입니다. DDD에 관련된 내용인데 이에 대해 정확한 개념을 학습한 채 작성한 것이 아니기 때문에 잘못된 내용을 알려주신다면 보는 즉시 수정하겠습니다. **JPA를 사용했기 때문에 글을 읽기 위해서는 JPA에 대한 기본적인 이해가 필요합니다. 2022. 10. ** 별도의 API가 필요하기 때문에 하나의 레포지토리로 하위 엔티티를 관리하는 것은 힘들다고 판단해 이 포스팅은 그 당시의 생각, 하나의 방법 정도로 읽어주시면 감사하겠습니다. 1. 별도의 레포지토리가 필요할까? 게시판을 만들때 댓글은 게시글의 하위에 속해있습니다. 이 경우 /api/posts/{postId}/comments/{commentId} 와 같이 API를 구성할 수 있는데요, 물론..
2022.07.31
-
[9월] 2주차 회고
본가에 일찍 내려왔다. 차가 막힐것 같기도하고, 또 최근 너무 안자고 안먹고 개발만 하다보니 쓰러질 것 같아 휴식차원에서 일찍 왔는데 덕분에 체력을 많이 회복했다. 세끼 다 먹은게 4-5개월만인데 잘 먹고 잘 자니까 개발할때도 집중이 잘되고 재밌었다. 운이 나쁘게(?) 내려온 당일부터 3일정도 태풍이 와서 하루종일 집에만 있었는데 얼른 태풍이 끝나고 밖에 나가고 싶다. 날씨가 잠잠(?)해져서 집 앞에 바다구경갔더니 아래처럼 돼 있더라... 산책로가 다 잠겨서 파도가 치고있었고 영상찍다 파도 씨게 맞은건 안 자랑이다^^ 1. 학습내용 - 데이터베이스 학습 - 프론트 배포 - 알고리즘 스터디 이번주는 데이터베이스에 많은 시간을 쏟았다. 쿼리를 점검하면서 튀는 부분을 잡고 조회성 쿼리를 위해 많은 부분을 튜닝..
2022.09.08
-
[9월] 1주차 회고
드디어 서비스가 출시됐다. 내가 만든 기능들이 실제로 보이니 너무 재미있었는데 중간중간 에러가 보여 이를 잡는다고 많은 시간을 사용했다. 비록 시간은 많이 썼지만 이 과정에서 문제해결방식에 대한 인사이트를 얻을 수 있었는데 자기가 사용하는 기술만 알고 있으면, 백엔드나 프론트 한 분야만 알고 있으면 결국 그 수준에서밖에 문제를 해결하지 못한다는 점이다. 백엔드가 프론트도 알다보면(그 반대의 경우에도), 다양한 경험을 하다보면 한가지 방법으로 문제를 해결하지 않아도 된다는걸 많이 느낀다. 즉, 어떤 부분에서 문제를 처리하면 더 빠르게 해결할 수 있는지를 알 수 있게 된다는 것이다. 그리고 테스트 코드를 작성하다보니 확실히 대부분의 오류를 잡을 수 있었는데 덕분에 백엔드로직에서 삐긋한 경우가 거의 없었다. ..
2022.09.02