첫 오픈소스 기여 후기

2024년 취업과 동시에 여러가지 목표가 있었는데, 그 중 하나인 오픈소스 기여를 매우 간단하게 성공했던 후기를 공유해보고자 한다. 많은 개발자들이 어렵게 생각하는 만큼, 나 역시 “나 같은 하찮은 개발자가 오픈소스에 기여할 수 있을까?” 고민 했었지만, 역시 일단 하고 두들겨 맞는게 가장 빠르게 목표를 달성할 수 있다고 생각하여 오픈소스에 기여할 방법을 찾아봤다.

오픈소스 기여 대상 탐색

처음에는 Github에서 Major한? 프레임워크(Vue.js, Spring Framework)를 대상으로 기여를 하려고 했으나, 이미 많은 사람들이 참여하고 있는 프로젝트들인 만큼 내가 기여할 수 있는 부분을 빠르게 찾기는 어려웠다. 그래서, SSAFY 자율 프로젝트 진행 중 피쳐플래깅 서비스로 레퍼런스에 이용했었던 Unleash라는 서비스를 보던 중, 충분히 납득할만한 기여사항을 발견하여 이 프로젝트의 문서화에 기여하기로 했다.

문서화 기여

해당 서비스는 피쳐 플래깅 서비스의 Java측 SDK로, ReadMe에 해당 서비스를 사용하기 위한 설정들이 적혀 있었는데, Maven 프로젝트 기준으로 세팅하는 방법이 적혀 있었다. 나는 평소 Gradle로만 프로젝트를 진행했었고, 많은 주니어 개발자들이 비슷한 경험을 했을것이라고 생각했다. 따라서, Gradle로 SDK 의존성을 추가하는 간단한 ReadMe를 작성했다.

아주 하찮은 수정사항이지만 해당 수정사항을 반영해야 하는 이유에 대해서 정성스레 PR을 올렸고, PR을 올린지 두시간만에 리뷰어님이 Approve와 함께 Merge를 진행해주셨다.

오픈소스 기여 후기

오픈소스에 기여하는것이 3년 이내에 해야할 버킷리스트에 포함되어 있을만큼 도전해보고 싶었던 일인데 정말 쉽고 빠르게 목표를 달성하여 기분이 좋기도 하고, 왜 진작 시도해보지 않았을까? 라는 생각도 든다. 비록 지금은 문서 수정이지만 앞으로 여러 오픈소스 프로젝트에 참여하고 국내 및 해외 개발자들과 소통하며 다양한 경험을 하고싶다.

'박현우' 카테고리의 다른 글

Github Foundations 자격증 후기  (3) 2024.09.28
2024 AWS Innovate Migrate, Modernize, Build 후기  (4) 2024.09.26
2024-06~2024-09 회고  (2) 2024.09.08
SSAFY[10기/전공/자바반] 수료 후기  (3) 2024.07.22
SAA-C03 자격증 후기  (0) 2024.07.20

SSAFY에서의 1년 회고하기

SSAFY 10기, 자바 전공반 합격 및 수료 후기

SSAFY 준비

에세이

23년 4월 중순에 SSAFY 서류 접수 기간이었던것으로 기억한다. 서류같은 경우 짧은 500자의 에세이를 작성했는데, 여러 블로그 글들을 참고하여 “내가 무엇을 잘한다” 라는 느낌 보다는 “내 부족한 점들을 SSAFY에서 얻어가겠다.” 라는 느낌으로 글을 작성했고, 서류 합격을 받을 수 있었다.

코딩 테스트

코딩 테스트의 경우 가끔씩 2~3 문제씩, SWEA, 백준 문제를 풀었다. 코딩테스트를 잘 하는 편은 아니지만 설렁설렁 준비했는데 두문제 다 맞았다. 크게 어려운 느낌은 없었다. 텅텅 비어있는 커밋 기록..

인터뷰 준비

면접 부터는 조금 어려웠는데, PT면접이 처음이라 면접스터디를 진행했다. 강민혁님 유튜브 영상을 많이 참고했고, 4차산업 관련된 IT 기술들에 대해서 조사하고 공부했었다. 인터뷰를 망치는 바람에 떨어졌었는데, 추가합격으로 SSAFY 10기 교육을 들을 수 있었다.

SSAFY 1학기

SSAFY 1학기 전공 자바반의 경우 기본적인 개발 역량과 더불어 알고리즘, Java Spring, Vue.js 개발에 대해서 배울 수 있었다. 강사님들마다 각각 교육하는 스타일이 다른 것 같았는데, 우리 강사님의 교육 방식은 많은 도움이 되었다. 첫번째로, 알고리즘 스터디를 권장하셔서 이전보다 알고리즘 역량이 많이 늘었던 것 같다. 두번째로, 기술 질문에 대해 정성스럽게 대답해주시고 경험에 기반한 설명과 함께 이론을 설명해주셔서 이해가 빠르게 되었다. 또한 1학기 막바지에는 프로젝트를 진행하는데, 이 프로젝트는 크게 도움이 되지는 않았던 것 같다. (2학기에 비해서..)

1학기는 아래의 키워드들로 얘기할 수 있을 것 같다.

  1. 알고리즘
  2. Spring Framework + Vue.js
  3. 공통 프로젝트

SSAFY 2학기

SSAFY 2학기는 전공, 비전공 관계없이 모여 3개의 프로젝트를 진행한다. 프로젝트는 담당 컨설턴트님과 실습코치님이 함께 개발 방향을 도와주는데, 성향에 맞는 컨설턴트님과 코치님을 만나는 것도 중요한 것 같다. 2학기의 경우에는 시간도 부족하고 취준도 함께 진행하다보니 갈등이 생기기도 하고, 힘든 시간이었던 것 같다. 나는 프로젝트 경험보다 서류 작성 방법, 기업 분석과 같은 전공 지식을 제외한 취업준비가 필요했기에, 3개의 프로젝트 중 두번째인 특화 프로젝트에서는 주로 취준에 집중 했었다. SSAFY에서는 이러한 점을 좋게 여기지 않았는데, 왜냐하면 SSAFY 2학기는 취업 준비를 하는 기간이 아니라 프로젝트를 진행하는 기간이기 때문이다. 아무튼 나의 경우 이 기간동안 취업 준비를 많이 했고, 서류를 합격하는데 많은 도움이 되었던 것 같다.

SSAFY 수료

SSAFY를 수료함과 동시에 취업이 되어서 생각지도 못한 외국계 기업에서 일을 하게 되었다. 또한 SSAFY 에서의 1년간 교육을 수료하면서 많은 것들을 느낄 수 있었다. 기업의 채용 과정과 같이 에세이, 코딩테스트, 면접을 통해 교육생을 선정하다 보니 상향 평준화된 인원들과 함께 개발을 진행할 수 있는 소중한 기회였다. 또한, 학생 신분임에도 현업 개발자로 일했던 나보다도 깊은 지식을 가지고, 또 개발에 대한 열정을 가지고 임하는 몇몇 교육생들로부터 큰 귀감을 얻기도 했다.

2년 이상의 경력을 포기하면서도 SSAFY에 온 것은 앞으로의 개발자로서의 경력을 발전시키는 데 큰 도움이 될 것이라고 생각한다. 또한, SSAFY에서의 경험을 정리하면서 앞으로 개발자로서의 커리어를 어떻게 이어 나갈지 간단하게 정리해 본다.

  1. back end 개발자로서의 역량을 높이기

1학기에는 평소 약점이라 생각했던 알고리즘 해결 능력을 높이는 데 주력했었다. 처음 알고리즘 수준은 프로그래머스 2단계 문제를 푸는 것도 어려울 만큼 알고리즘 지식이 부족했었기 때문에 교육장 내에서 끝자락에 있을 만큼 수준 차이가 있었고, 이를 줄이기 위해서 알고리즘 문제를 풀이하는 데에 많은 시간을 보낸 것 같다.

또한, 2학기에는 3개의 프로젝트 중 2개는 인프라 담당, 1개는 프론트엔드 담당으로 개발을 진행했었기 때문에, SSAFY에서 처음에 목표했던 백엔드 개발자로서의 경력을 쌓기 위한 준비를 많이 하지 못한 것이 아쉽다.

따라서, 입사 후에는 기존 서비스의 백엔드 파트 리팩토링을 통해 백엔드 개발자로서의 역량을 더 채우고자 한다.

  1. 눈에 보이는 것을 남기기

1년간 SSAFY 교육과정을 이수하면서 느낀 점 중 하나는 눈에 보일만한 이력을 남기는 것의 중요성이다. SSAFY에서 교육받기 전에는 항상 스스로 부끄럼 없이 개발만 잘하면 된다고 생각했었는데, 아무리 실력이 뛰어나다고 해도 스스로를 어필하고 보여주지 못하면 이러한 노력에 대해서 결과를 극대화할 수 없다고 생각했다.

프로젝트, 블로그 포스팅, 자격증과 같이 기록이 남을 수 있는 결과물을 내는 것도 신경을 쓰며 개발을 진행하도록 하자. 2024년 하반기에는 개발 관련 자격증을 하나 취득하는 것을 목표로 진행하도록 하자. (리눅스 마스터 따기)

  1. 더 성실해지기, 비즈니스 매너를 갖추기

SSAFY 교육과정을 진행하면서 과음으로 인하여 지각을 하기도 했고, 과제를 하느라 제때 퇴실 처리를 하지 못할 때도 있었고, 일찍 와서 공부하느라 지각 처리되기도 했었다. 이 역시 내가 아무리 공부했던, 과제를 했든 간에 출결에 이슈가 발생한다. 이러한 문제가 발생하지 않도록 더욱 출결과 같은 기본적인 매너를 잘 지킬 수 있도록 노력해야 할 필요가 있다.

또한, 교육과정은 쉬는 시간 없이 프로젝트를 진행하고, 얼어붙은 취업시장에서 취업 준비까지 병행했었다. 모두가 지치고 힘든 상황에서 상대방을 배려해 주는 것 역시 개발하면서 꼭 필요한 덕목이라고 생각한다. 업무가 미뤄지는 것, 이해되지 않는 코드를 보며 기분이 태도가 되어 팀원들에게 매너를 갖추지 않고 말했던 것에 대해 팀원들에게 진심으로 미안하게 생각한다. 다시는 이러한 실수를 하지 않도록 주의하도록 하자.

'박현우' 카테고리의 다른 글

Github Foundations 자격증 후기  (3) 2024.09.28
2024 AWS Innovate Migrate, Modernize, Build 후기  (4) 2024.09.26
2024-06~2024-09 회고  (2) 2024.09.08
첫 오픈소스 기여 후기  (1) 2024.07.29
SAA-C03 자격증 후기  (0) 2024.07.20

자격증따자

최근 이직을 하면서 취준 기간동안 스스로 개발 능력의 부족함 보다는 내가 잘 할 수 있는 기술임에도 어필 할 수 있는 부분들이 부족해서 아쉬웠던 순간들이 있었다. 2024년 하반기의 목표로 나는 자격증을 2개 따는것을 목표로 하고 있는데, 그중에 꼭 그 타이틀을 가지고 싶었던.. AWS 자격증(SAA-C03)을 따기위해 공부했던 방식을 공유하고자 글을 작성한다.

원래는 CLF-C02 (Practioner) 자격증을 먼저 따려고 했는데, 초급자용이라고 해서 좀더 쎄보이는? Associate 자격증을 따기로 마음을 바꿨다..

또한 나는 실무에서 AWS 서비스를 사용했던 경험은 없고 EC2, RDS, S3 서비스로 SSAFY에서 프로젝트를 진행했던 경험만 있었다.

취득과정

나는 Udemy 강의 없이 ExamTopic과 구글링만을 이용하여 자격증을 취득했다.

약 2주정도 CLF-C02 공부를 했었는데, Udemy강의를 3시간 정도 듣다가 너무 지루해서 덤프를 풀면서 모르는 서비스에 대해 따로 조사하면서 공부했다. 자격증 공부를 위한 공부?가 되지 않게 하려고 최소한 어떤 상황에 사용하는 서비스이고, 어떤 서비스와 결합했을때 상성이 좋은지를 함께 보면서 공부했던 것 같다.

2주 후 부터는 SAA-C03를 공부하기로 마음을 틀었는데, 이때부터 약 4주정도 공부했고 공부했던 방식은 동일하게 Udemy 강의를 보지않고 ExamTopic 덤프를 풀면서 공부했다. SAA-C03의 경우 ExamTopic에 900개 가량의 문제가 등록되어 있는데, 나는 550번 까지 풀어보고 시험을 쳤다.

또한, LG CNS, 베스핀 글로벌, 농심 NDS, 메가존 클라우드의 기술 블로그를 보면서 서비스를 이해하는데 많은 도움을 받았던 것 같다.

회사에서 내부 프로젝트와 병행하며 공부했었기에 하루 평균 4시간 정도 공부했고, 주말이나 퇴근 후에는 전혀 공부를 하지 않았다.

https://www.examtopics.com/exams/amazon/aws-certified-solutions-architect-associate-saa-c03/view

시험당일

나는 오프라인으로 시험을 봤고, 당일 약 2시간정도 덤프 문제를 훑어 봤다. 여권, 신용카드, 주민등록증을 챙겨서 시험시간 약 20분 전에 시험장에 도착했다. 근데 시험장에서 AWS 시험 접수 시에 등록한 이름과 신용카드 상의 이름이 달라서 약간의 문제가 있었다. 시험을 치는데에는 문제 없었고, 감독관님께서 시험을 치고난 후 AWS 계정의 이름을 바꾸면 된다고 하셨다. 오후 1시 30분 부터 약 50분 가량 시험을 쳤고, 오후 7시에 바로 합격 메일을 받을 수 있었다! 

자랑

점수가 높진 않지만, 정보처리기사 이후 두번째로 IT 자격증이 생겨서 기분이 좋다! 2024년도에 또 하나의 자격증을 따기위해 노력해보자!! 

'박현우' 카테고리의 다른 글

Github Foundations 자격증 후기  (3) 2024.09.28
2024 AWS Innovate Migrate, Modernize, Build 후기  (4) 2024.09.26
2024-06~2024-09 회고  (2) 2024.09.08
첫 오픈소스 기여 후기  (1) 2024.07.29
SSAFY[10기/전공/자바반] 수료 후기  (3) 2024.07.22
LightSwitch Logo

 

안녕하세요. LightSwitch 서비스에서 인프라 개발에 참여했던 박현우 입니다. 

LightSwitch는 오픈소스 피쳐플래깅 솔루션으로, 도커 이미지 형태로 누구나 쉽게 서비스에 피쳐 플래깅을 도입할 수 있는 서비스입니다.

저는 인프라와 자바스크립트 SDK 개발자로서 LightSwitch 팀에서 함께 프로젝트를 진행했는데요, 서비스의 인프라를 구성하면서 겪은 문제를 공유하고자 정리해 보았습니다.

Nginx 구성하기

인프라 담당으로서 첫 번째로 담당했던 서비스 SPOPARTY에서는 호스트 운영체제에 바로 Nginx를 설치하여 사용했는데, LightSwitch 서비스에서는 nginx도 컨테이너로 관리하는 방식을 이용하게 되었습니다.

따라서, 컨테이너화된 nginx에서 리버스 프록시를 활용하여 backend 서버로 요청을 리다이렉트 해야 할 필요성이 있었습니다. 팀원들과의 대화 및 구글링을 통해 컨테이너 이름으로 리버스 프록시를 활용할 수 있겠다고 생각했지만, 생각만큼 쉽게 적용되지 않았습니다.

이슈 상황

첫번째 이슈 해결

먼저 Nginx와 backend의 네트워크 연결을 위해 첫 번째로 nginx에서 backend 컨테이너의 이름으로 리버스 프록시 설정을 했었습니다. 하지만 docker 컨테이너 생성 시 기본적으로 bridge 네트워크에 소속되며, 기본 네트워크에서는 컨테이너 기반의 DNS 해석이 지원되지 않기 때문에 컨테이너 간 직접적인 연결이 불가능한 상황이었습니다. 따라서 backend 컨테이너로의 직접적인 연결을 하지 않고, 호스트 운영체제의 도메인으로 redirect 시켜 문제를 해결했습니다.

첫번째 이슈 해결 문제점

하지만, 호스트 운영체제를 통해 nginx 컨테이너와 backend 컨테이너가 통신을 하게 된다면 불필요한 네트워크 요청이 추가되게 됩니다.

// 현재 문제점
외부 요청 -> Host OS -> Nginx -> Host OS -> Backend
// 개선 한다면 아래와 같이 될 것이다.
외부 요청 -> Host OS -> Nginx -> Backend

두번째 이슈 해결

Nginx 컨테이너에서 backend 컨테이너로 직접적으로 요청을 전달하기 위해서는 내부 IP를 이용할 수 있을 것 이라 생각하여 컨테이너의 IP로 직접 요청을 전달하도록 수정했습니다.

docker inspect ${Docker Container 이름}

"IPAddress": "172.17.0.2",

위 명령어를 통해 얻어낸 IP 주소를 nginx의 proxy_pass에 직접 설정하여 요청을 전달했고, 정상적으로 동작하는 것을 확인했습니다.

두번째 이슈 해결 문제점

하지만 위와 같은 방식을 이용한다면 컨테이너를 재시작 하는 경우 Nginx 설정을 변경해야 하는 번거로움이 있었습니다. 컨테이너에 할당되는 IPAddress는 고정된 값이 아니기 때문입니다.

세번째 이슈 해결

많은 예제 글에서 항상 docker-compose로 컨테이너들을 묶고, docker-compose 파일 내에서 network 설정을 통해 연결을 경우가 많이 있었는데, 동일한 방법으로 이슈를 해결할 수 있었습니다.

그렇다면 어떻게 docker-compose와 network 설정하는 방법으로 통신이 가능한 것인지 궁금했고, 이를 해결하기 위해서는 Docker Network에 대한 이해가 필요할 수 있음을 깨달을 수 있었습니다.

Docker Network란?

도커 네트워크(Docker Network)란 Docker 컨테이너 간의 통신을 관리하고 격리하기 위한 기능을 제공하는 것입니다.

컨테이너화된 애플리케이션은 여러 개의 컨테이너로 구성될 수 있는데, 이들 컨테이너가 서로 통신하고 데이터를 주고 받아야 할 경우가 있습니다.

도커 네트워크를 통해 이러한 컨테이너 간 통신을 쉽게 설정하고 관리할 수 있도록 도와줍니다.

정리하자면, 같은 호스트 내에서 실행 중인 컨테이너 간 연결할 수 있도록 돕는 논리적 네트워크 개념이다.

Docker Network Driver의 종류

  • bridge : 호스트 컴퓨터 내에서 여러 컨테이너들이 서로 소통할 수 있도록 해줍니다.(기본 네트워크 드라이버)
  • host : 컨테이너와 Docker 호스트 간의 네트워크 격리를 제거합니다.
  • none : 컨테이너를 호스트 및 다른 컨테이너로부터 완전히 격리합니다.
  • overlay : 오버레이 네트워크는 여러 Docker 데몬을 함께 연결합니다.
  • ipvlan : ipvlan 네트워크는 IPv4 및 IPv6 주소 지정에 대한 완전한 제어를 제공합니다.
  • macvlan : 컨테이너에 MAC 주소를 할당합니다.

실제 네트워크 확인해보기

현재 네트워크 목록을 확인하기 위해서 아래의 명령어를 통해 확인할 수 있습니다.

docker network ls

다양한 네트워크가 존재하는 것을 확인할 수 있습니다. 이 중 기본으로 적용되는 네트워크인 bridge에 포함된 컨테이너들을 확인해보겠습니다.

docker network inspect bridge

현재 bridge 네트워크에 소속된 컨테이너들을 확인할 수 있으며, Options에서 기본 네트워크라는 사실을 알 수 있습니다.

위를 보면 컨테이너의 이름이 포함되어 있음에도 왜 Docker는 bridge 네트워크에 대해 DNS 기반 해석을 제공하지 않는 걸까요? 아시는 분은 댓글 좀…

마지막으로, 세 번째 이슈 해결에서 말했던 docker-compose로 실행 시에는 어떻게 DNS 접근이 가능했던 것인지 알아봅시다.

docker-compose up 명령어를 통해 컨테이너화 하면, docker network ls 를 통해 네트워크를 확인 시 아래와같이 새로운 network가 생성됩니다. 네트워크 생성 정책은 폴더명_default로 생성되는 듯합니다.

생성한 네트워크에 소속된 컨테이너 목록을 확인해보겠습니다.

네트워크 내에 docker-compose에 포함된 컨테이너들이 모두 있는 것을 확인할 수 있습니다.

문제의 원인 정리

즉, docker-compose로 컨테이너들을 함께 관리할 경우 기본적으로 하나의 bridge 타입 네트워크가 생성되며, 기본 네트워크가 아니기 때문에 DNS 해석이 가능하여 문제를 해결할 수 있었던 것입니다. 만약 DNS 해석이 필요한 경우 사용자 정의 Network Driver를 생성하거나, link 설정 또는 docker-compose로 컨테이너를 관리하는 것이 선행되어야 합니다.

마무리

Docker에 대한 이해가 부족한 상태로 개발을 진행했던 것이 부끄럽게 느껴지기도 하지만, 실제로 트러블 슈팅을 경험하면서 적은 글이 독자님들에게 더 깊은 공감을 끌어낼 수 있다고 생각하여 부족한 실력에도 글을 공유합니다.

또한, 재배포 없이 쉽게 A/B 테스팅, 타겟 테스팅을 지원하는 오픈소스 피쳐플래깅 서비스 LightSwitch에도 많은 관심 부탁드립니다.

감사합니다.

LightSwitch 레포지토리 바로가기

Reference

Networking overview

Docker 네트워크 사용법

Docker Network 구조(4) - container link 구조

Bridge network driver

개요

JavaScript 라이브러리를 개발하며 React, Vue와 같은 프레임워크를 제외하고 빌드와 관련된 여러 도구들의 관계를 정리하고자 포스팅한다.

1. WebPack

1 - 1. 웹팩이란?

웹팩은 웹 어플리케이션에서 사용하는 수많은 리소스를 하나의 파일로 병합 및 압축 해주는 모듈 번들러이다.

1. 자바스크립트에서의 모듈

자바스크립트에서의 모듈이란 특정 기능을 가진 코드들을 하나의 파일로 관리하는 것

// date.js
export const getDate() => new Date().valueOf();

2. 웹팩에서의 모듈

웹팩에서의 모듈은 자바스크립트 모듈 뿐만이 아니라 HTML, JavaScript, Images, Font등 웹 애플리케이션을 구성하는 모든 자원들을 모듈이라고 한다.

3. 모듈 번들링

출처 : https://joshua1988.github.io/webpack-guide/webpack/what-is-webpack.html#%EC%9B%B9%ED%8C%A9%EC%97%90%EC%84%9C%EC%9D%98-%EB%AA%A8%EB%93%88

위 이미지처럼 여러 모든 모듈을 하나의 파일로 병합하는 행위를 모듈 번들링이라고 한다.

1 - 2, 웹팩의 등장 배경

  1. 파일 단위의 자바스크립트 모듈 관리의 필요성
  2. 웹 애플리케이션의 빠른 로딩 속도와 높은 성능

1. 파일 단위의 자바스크립트 모듈 관리의 필요성

자바스크립트의 변수 유효 범위 (스코프)는 기본적으로 전역 범위를 기본으로 가진다. 즉 어디서든 변수에 접근할 수 있다는 말이다.

파일 단위로 자바스크립트 코드를 관리할 때 모듈화 하지 않으면 어떤 문제가 발생하는지 예를 들어 알아보자.

// module1.js
var foo = 10

// module2.js
foo = 20

// main.html
<body>
<script src="./module1.js"></script>
<script src="./module2.js"></script>
<script>console.log(foo) // 20 </script>
</body>

위 코드에서 foo는 전역변수이기 때문에 어디서든 접근할 수 있고 값을 변경할 수 있다. 프로젝트가 커지고 이러한 변수가 많아진다면 남이 사용한 변수를 실수로 변경할 수 있는 문제등이 발생한다.

그리고 script의 순서를 바꾸면 foo의 값도 바뀌게 된다.

// main.html
<body>
<script src="./module2.js"></script>
<script src="./module1.js"></script>
<script>console.log(foo) // 10 </script>
</body>

이러한 문제를 해결하기 위해 javascript 파일을 모듈로 관리하게 되었고, CommonJS, ESModule 등의 모듈 시스템이 만들어졌다.

웹팩은 다양한 모듈화 방식 중 ESModule 방식을 사용한다.

2. Vite

1. Vite란?

Vite는 네이티브 자바스크립트 모듈 (ESModule)을 기반으로 한 데브 서버이다. 이미 현대 프런트엔드 개발 생태계는 웹팩을 중심으로 개발 환경과 배포 시스템이 구축되어 있습니다. 그런데 왜 비트와 같은 도구들이 나왔을까요? 그 이유는 웹팩을 사용할 때보다 훨씬 더 빠르게 개발하고 배포할 수 있기 때문입니다. 왜 빠른지 이해하기 위해서는 먼저 번들링과 자바스크립트 네이티브 모듈을 이해해야 합니다.

2. ESM (자바스크립트 네이티브 모듈)

ESM은 모듈화 문법인 import, export를 별도의 도구 없이 브라우저 자체에서 소화해 낼 수 있는 모듈 방식을 의미합니다. 만약 아래와 같은 코드를 웹팩과 같은 번들러 없이 브라우저에서 실행하면 에러가 발생합니다.

// app.js
import { sum } from "./math.js"

console.log(sum(10,20))

<script src="./app.js"></script>

기존의 브라우저에서는 importexport를 해석할 수 있는 능력이 없었습니다. 하지만, 이제는 script 태그에 아래와 같이 type="module" 속성을 추가하면 정상 동작하는 것을 볼 수 있습니다.

<script type="module" src="./app.js"></script>

브라우저에서 importexport를 소화할 수 있는 능력이 바로 ESM입니다.

3. Vite 특징

비트는 로컬에서 개발할 때 번들링을 하지 않고 ESM 방식을 사용하기 때문에 로컬 서버 구동 속도가 매우 빠릅니다. 500개 정도 되는 모듈을 갖고 있는 웹 서비스를 웹팩 데브 서버 (opens new window)와 비트로 비교해 본다면 실행 시간이 20 ~ 30배 이상 차이가 납니다. 웹팩 데브 서버는 처음 로컬 서버를 시작할 때 관련 있는 모듈들을 번들링 해서 메모리에 적재하는 시간이 필요하기 때문에 당연히 어느 정도의 시간이 필요합니다. 반면 비트는 번들링을 하지 않고 바로 서버를 실행하기 때문에 명령어를 실행함과 동시에 서버가 바로 구동됩니다.

3. Babel

1. Babel 이란?

Babel is a JavaScript compiler.

정확히는 babel 은 javascript 로 결과물을 만들어주는 컴파일러입니다.소스 대 소스 컴파일러 (transpiler)라고 불립니다.

2. ESNext and Legacy..

지금도 프론트엔드는 너무나도 빠르게 발전되고 있습니다. 심지어 최신 브라우져 조차도 지원하지 못하는 문법과 여러 기술들이 출연하고 있습니다. 새로운 ESNext 문법을 기존의 브라우져에 사용하기 위해서 babel 은 필수적입니다. 모든 사람들이 새로운 브라우져를 쓰면 좋겠지만, 슬프게도 아직도 많은 사람들이 예전 브라우져 예전 OS 를 사용하고 있습니다.

 

이런 하위 호환성은 외면하기에는 쉽지 않습니다. 이또한 babel 을 쓰는 강력한 이유가 됩니다. (babel-polyfill 부분을 참고하세요!)

3. polyfill

폴리필(polyfill) 은 개발자가 특정 기능이 지원되지 않는 브라우저를 위해 사용할 수 있는 코드 조각이나 플러그인을 의미 합니다. 브라우저에서 지원하지 않는 기능들에 대한 호환성 작업을 채워 넣는다고 해서 polyfill 이라고 칭합니다.

babel 은 이러한 polyfill 을 손쉽게 지원하기 위해 babel-polyfill 기능을 지원합니다.
아까 이미 문법을 컴파일 해서 javascript 로 compile 한다고 했는데… 왜 polyfill 이 필요할까요?

babel 을 사용한다고 최신 함수를 사용할 수 있는 건 아닙니다. babel 은 문법을 변환하여 javascript 로 변환하는 transpiler 역할만 할 뿐입니다.

앞에서 설명한대로 polyfill 은 프로그램이 처음에 시작될 때 지원하지 않는 기능들을 추가하는 것입니다. 즉, babel 은 컴파일시에 실행되고 babel-polyfill 은 런타임에 실행되는 것입니다.

Reference

[Webpack] 웹팩이란? 웹팩을 사용하는 이유

Introduction | Cracking Vue.js

babel 이란 무엇인가?

개요

vue를 통해 front-end측 로직 처리 중 이상한 오류가 발생하여 공유한다. api 호출 시 특정 상황에서 CORS 오류가 발생하는데, CORS 오류와 함께 504 에러가 발생하는 것이다. 서버측 CORS 설정은 한참전에 완료되었기 때문에 다른 문제라고 바로 생각이 들어서, CORS + 504 에러가 발생하는 경우로 검색해봤다.

아래의 블로그가 나와 비슷한 현상이 발생하는것 같아서 적용해보니 해결할 수 있었다.

504 (Gateway Timeout) 에러 해결 (AWS EB, Node.js)

또한 CORS와 함께 504 에러가 발생하는 이유는 아래의 글에서 확인할 수 있었다.

https://github.com/kubernetes/ingress-nginx/issues/2134

정리

문제의 원인은 nginx측에서 기본적으로 proxy 연결 시간이 60초로 설정되는데, 이 시간이 넘어가면서 nginx측에서 504 에러를 리턴한 것이다. 그와 더불어 504 에러를 리턴할 때에 header에 CORS 관련 정보가 포함되지 않아 두 오류가 동시에 발생하는 것이다.

따라서, nginx측 오류만 수정한 후 다시 시도하니 성공적으로 API를 요청할 수 있었다.

개요

2024-03-15일 저녁에 공통 프로젝트 팀원들과 회식을 하면서 비동기 처리 관련 글을 포스팅 했던 얘기가 나왔었는데, 여기서 내가 글을 썼음에도 여러가지 부족한 점들이 느껴져서 조금만 더 정리해본다. 그 중 자바스크립트 에서 비동기 처리를 어떻게 하는것인가에 대해서 얘기하다 어물쩡 거려가지고 좀 부끄러웠다. 어떻게 진행되는지 한번 보자.

동기와 비동기

동기

동기(Synchronous)는 순차적인 흐름을 가진다. 하나의 작업이 실행되는 동안, 뒤의 다른 작업들은 그대로 멈춘 채 자신의 차례가 오기까지를 기다린다.

이 일련의 과정 중에서 동기식 프로그램의 특징을 알 수 있다. 손님이 주문을 요청하면, 점원은 그 요청에 대한 처리를 거처 응답을 내놓는다. 손님은 응답이 돌아오기까지 카운터를 떠나지 않고 기다린 뒤, 응답을 받고 나서야 다음 행동을 할 수 있다.

한번에 하나의 태스크를 수행하는 것은 싱글 스레드에서 코드가 동작할 경우이다. 요청에 대한 처리를 할 수 있는 주체가 하나만 존재하기 때문에, 위와 같이 순차적인 흐름을 가질 수 밖에 없다.

비동기

비동기(Asynchronous)는 반대로, 어떠한 요청을 보내면 해당 요청의 응답에 관계없이 바로 다음 동작이 실행된다. 즉, 실행중인 작업이 끝나기를 기다리지 않고 바로 다음 작업으로 넘어갈 수 있다.

점원들의 작업은 비동기식으로 처리되고 있다. 점장은 점원1에게 요청을 보낸 후, 응답을 받지 않은 상태에서 점원2에게 다른 요청을 보낸다. 둘 다 완료 보고라는 응답이 돌아오지 않았지만 점장은 신경쓰지 않고 다음 작업으로 넘어갈 수 있다.

멀티 스레드 방식에서는 요청을 처리할 수 있는 작업 단위가 여러 개이기 때문에, 하나의 태스크가 끝나기를 기다리지 않아도 다음 실행을 할 수 있게된다.

자바스크립트 엔진이란?

자바스크립트의 코드를 해석하고 분석하고 실행하는 인터프리터, 즉 해석기이다. 주로 웹 브라우저에서 사용된다. (자바스크립트 엔진은 브라우저 안에 내장되어 있다.) 엔진에 의해 해석하기 때문에 컴파일 과정일 불필요하다. 웹 브라우저에서 곧바로 해석하고 실행한다.

브라우저별 엔진 종류

  • V8 : 자바스크립트 대표 엔진이다. 구글에서 개발했으며, 크롬과 노드js에서 사용한다.
  • 웹킷 : 애플에서 개발한 오픈소스 엔진이고, 사파리에서 사용한다.
  • 스파이더 몽키, 차크라 등등

자바스크립트 엔진 구조

자바스크립트 엔진은 크게 메모리 힙과 콜 스택으로 나눌 수 있다. 이벤트 루프, 콜백 큐와 같은 엔진 외부 요소들과 함께 코드를 실행한다.

메모리 힙과 콜스택

메모리 힙

객체, 함수, 참조 타입과 같은 데이터를 저장하는 공간.

콜 스택

코드를 실행할 때 코드 안의 실행 순서를 기록하고 순서대로 코드가 실행될 수 있도록 도와주는 스택이다. 자세히 말하자면 함수를 호출 시 함수 실행 컨텍스트가 스택에 쌓이면서 순서대로 실행된다.

<aside> 💡 실행 컨텍스트란? 브라우저가 HTML 문서를 해석할 때, <script> 태그로 감싸진 자바스크립트 코드 또는 onClick과 같은 속성을 가진 태그를 만나면 이것을 자바스크립트에 보낸다. 그리고 자바스크립트 엔진이 브라우저가 넘겨준 코드를 변환하고 실행시키기 위해 특별한 환경을 구성하는데, 이것을 실행 컨텍스트 라고한다.

</aside>

자바스크립트는 어떻게 비동기 실행이 가능할까

자바스크립트 코드가 비동기 실행으로 보여지게 만들어 주는 것은 바로 브라우저이다. 브라우저는 자바스크립트 엔진 외에도 태스크 큐, 이벤트 루프, Web API를 가지고 있다.

  • 태스크 큐 : 비동기 함수의 콜백 함수가 임시 보관되는 공간
  • 이벤트 루프 : 자바스크립트 엔진의 콜스택이 비어있는지, 태스크 큐에 대기 중인 함수가 있는지 확인
  • Web API : 브라우저 환경에서 제공하는 API
    • ex) setTimeout, DOM API, AJAX통신, Canvas, WEBGL, 오디오 또는 비디오 API

비동기 실행에서 브라우저의 역할

자바스크립트 엔진은 싱글 스레드로 동작하지만, 브라우저 엔진은 멀티 스레드로 동작한다. 멀티 스레드로 동작하는 브라우저가 제공하는 Web API를 함께 사용하여 비동기 동작을 구현할 수 있는 것 이다.

자바스크립트 엔진이 코드를 읽어 실행하는 과정

  1. 자바스크립트 엔진이 실행 컨텍스트를 차례대로 콜스택에 쌓는다.
  2. 쌓인 코드들이 순서에 따라 콜스택을 빠져나가며 실행된다.
  3. 코드 중 비동기 함수, 즉 setTimeout 또는 setInterval, 이벤트 핸들러와 같은 Web API 함수를 만나면 자바스크립트 엔진이 이들을 Web API로 보낸다.
  4. Web API는 전달받은 함수의 콜백 함수를 꺼내어 태스크 큐로 보낸다. 이때 딜레이 인자가 잇는 경우는 딜레이 시간만큼 Web API에서 대기하게 된다.
  5. 콜스택에 있던 함수들의 실행이 모두 완료되어 빈 공간이 되면, 이벤트 루프가 태스크 큐에서 대기중인 콜백함수 하나를 콜스택에 넣어 바로 실행시킨다.
  6. 다시 콜스택이 비면 이벤트 루프가 태스크큐가 빌때까지 위의 동작을 반복

결론

자바스크립트엔진은 싱글 스레드가 맞다. 그리고 멀티 스레드로 동작하는 브라우저의 도움이 있기에 비동기 통신을 효율적으로 처리할 수 있는것이다. 회식 하면서 갑자기 헷갈렸던 부분은 “자바스크립트 엔진은 어떻게 싱글 스레드이면서 이벤트 루프를 돌며 WEB API가 실행한 결과를 받아오나” 였는데, 브라우저가 이벤트 루프를 관리하기 때문에 그랬던 것으로 정리할 수 있겠다.

Windows 10 에어팟 케이스 충전 불가능 현상

종종 Windows 10 PC에 에어팟을 연결하여 충전을 하거나 연결하는데, 에어팟 케이스가 충전되지 않는 현상이 있어 이를 해결하는 방법을 적어본다.

Windows 키를 눌러 Bluetooth 및 기타 장치를 입력한 후 확인해보면 오디오에 연결된 에어팟의 이름과 기타 디바이스에 에어팟 케이스가 함께 표시되는데, 나의 경우 이 에어팟 케이스 아래에 드라이버 문제라는 텍스트가 함께 표시 되는 문제가 있었다. 

해결방법

해결방법은 아주 간단했다. 먼저 기타 디바이스에 표시된 AirPod Case 장치 제거 버튼을 클릭하여 장치를 제거해주고, 오디오 카테고리 아래에 있는 OO's AirPods 도 동일하게 장치 제거 버튼을 클릭해준다.

이후 에어팟을 다시 PC와 페어링 하여 연결 이후 정상적으로 충전할 수 있었다.

 

개요

SSAFY 2학기 특화 프로젝트에서 front-end를 담당하며 생긴 trouble shooting 로그를 남겨본다.

책을 넘기는 듯한 ui를 구현하기 위해 turn.js 라이브러리를 활용했으나, 환경적인 문제로 인하여 jquery 적용에 어려움을 느꼈다. 이를 해결한 방법을 정리해보자.

환경

  • vue ^3.3.0
  • vuetify ^3.0.0
  • typescript
  • vite

JQuery 적용하기

먼저 JQuery 적용 및 turn.js 사용을 위해 js 파일을 다운로드 한 후 index.html 파일에 추가해줬다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="./src/assets/logo-1.svg" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>동동주</title>
  </head>

  <body>
    <div id="app"></div>
    <script src="src/js/jquery-3.7.1.min.js"></script>
    <script src="src/js/turn.min.js"></script>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

여기까지 설정 후 바로 npm run dev 명령어를 통해 실행 시 정상적으로 표시되지만 빌드하여 서버에 업로드 시 아래의 오류 발생하며, turn.js 라이브러리가 정상 적용되지 않은것처럼 표시된다.

index-Cb6_uoSS.js:13 ReferenceError: $ is not defined
    at index-Cb6_uoSS.js:29:67165
    at index-Cb6_uoSS.js:13:11187
    at B3 (index-Cb6_uoSS.js:13:38)
    at T1 (index-Cb6_uoSS.js:13:109)
    at c.__weh.c.__weh (index-Cb6_uoSS.js:13:11068)
    at pn (index-Cb6_uoSS.js:13:1502)
    at Hn (index-Cb6_uoSS.js:13:1798)

원인파악

빌드 시 발생한 문제로 보이기 때문에, 관련 키워드로 검색 시도하여 아래의 게시글 발견

게시글의 댓글에서 script 태그에서 type=”module” 설정 이후 동작했다는 글이 있었지만, 동작하지 않았다.

ReferenceError: $ is not defined, Jquery Import with vite

또다른 게시글을 통해 방법을 찾아봤다. Jquery는 vite를 위해 일반적인 방식을 허용하지 않는다는 내용이 있었다. 또한 이를 위해서는 롤업 플러그인 삽입을 통해 동작한다는 것이다.

I am having problem using jquery with Vite. Used rollup-plugin-inject but still couldn't seem to make it work

Setup jQuery on Vite

이를 위해 아래의 명령어 실행 후 vite.config.mts 파일에 jquery를 추가하는 코드 작성했다.

$ npm i @rollup/plugin-inject --save-dev
$ npm i jquery
import inject from "@rollup/plugin-inject";
...

inject({
      jQuery: "jquery",
      "window.jQuery": "jquery",
      $: "jquery",
    }),

또한 rollup.config.mts 파일 생성 후 아래의 코드 추가

import inject from "@rollup/plugin-inject";

export default {
  input: "src/main.ts",
  output: {
    dir: "output",
    format: "cjs",
  },
  plugins: [
    inject({
      Promise: ["es6-promise", "Promise"],
    }),
  ],
};

이후 build한 파일을 서버를 업로드하여 jquery를 사용할 수 있었지만, 반대로 develop 환경에서는 다시 오류가 발생했다.

Uncaught (in promise) TypeError: $(...).turn is not a function
    at StoryBook.vue:8:21
    at chunk-2FDUVFJ5.js?v=4baee24c:4317:88
    at callWithErrorHandling (chunk-2FDUVFJ5.js?v=4baee24c:1660:19)
    at callWithAsyncErrorHandling (chunk-2FDUVFJ5.js?v=4baee24c:1667:17)
    at hook.__weh.hook.__weh (chunk-2FDUVFJ5.js?v=4baee24c:4297:19)
    at flushPostFlushCbs (chunk-2FDUVFJ5.js?v=4baee24c:1834:41)
    at flushJobs (chunk-2FDUVFJ5.js?v=4baee24c:1872:5)

해결방안

시간 관계상 해당 버그를 찾는데 너무 오랜 시간을 사용할 수 없으므로 우선 빌드 환경에서는 파일을 바꿔치는 방식을 이용하던지 해야할 것 같다.

Reference

[ Vue ] vue 프로젝트에 Jquery 설치하기

[Vue] jQuery 사용하기 #eslint 설정

Setup jQuery on Vite

ReferenceError: $ is not defined, Jquery Import with vite

I am having problem using jquery with Vite. Used rollup-plugin-inject but still couldn't seem to make it work

개요

평소 사용법만 알았던 어노테이션에 대해서 자세히 알아보고 직접 어노테이션을 만들어보자.

어노테이션이란

자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종

  • @ 기호를 붙여 사용한다.
  • JDK 1.5 이상 버전에서 사용가능하다.
  • 클래스 파일에 임베디드 되어 컴파일러에 의해 생성된 후 자바 가상머신에 포함되어 작동한다.

Annotation의 용도

  • compiler를 위한 정보 : Annotation은 컴파일러가 에러를 감지하는데 사용
  • 컴파일 시간 및 배포 시간 처리 : Annotation 정보를 처리해 코드, XML 파일 등을 생성
  • 런타임 처리 : 일부 Annotation은 런타임에 조사됨

Annotation의 종류

  • Built in Annotation : 자바에서 기본 제공하는 어노테이션 ex. @Override, @Deprecated
  • Meta Annotation : 커스텀 어노테이션을 만들 수 있게 제공된 어노테이션 ex. @Retention, @Target
  • Custom Annotation : 개발자의 의도에 의해 생성된 어노테이션

어노테이션 생성 방법

인터페이스 선언과 동일하나 @ 기호를 앞에 붙여준다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) // Runtime까지 해당 어노테이션을 삭제하지 않음
@Target(ElementType.TYPE) // 
public @interface MyAnnotation {
	int type() // 어노테이션 선언시에 필요한 데이터를 여기다 선언하기
	// 클래스, 메서드 또는 필드에 선언한다면, @MyAnnotation(type = 1) 이런 형식으로 사용됨
}

@Retention

어노테이션이 언제까지 살아있을 것인지를 정하는 어노테이션

  • RetentionPolicy.SOURCE : 소스코드(.java) 까지 남아있는다.
  • RetentionPolicy.CLASS : 바이트코드(.class) 까지 남아있는다.
  • RetentionPolicy.RUNTIME : 런타임까지 남아있는다.

어노테이션 활용하기

Annotation Processor를 통해 어노테이션을 처리할 수 있다.

컴파일 시점에 끼어들어 특정한 어노테이션이 붙어있는 소스코드를 참조해서 새로운 소스코드를 만들어 낼 수 있다

이를 확인해보기 위해 커스텀 어노테이션을 만들어 보자.

커스텀 어노테이션

필자의 경우 메서드의 성능 측정을 위한 어노테이션을 한번 만들어 봤다.

패키지를 생성하기에 앞서 패키지 구조를 아래와 같이 설정 했다. 한 프로젝트 내에서 사용가능한 자료를 찾지 못했다.

root
├─ annotation
└─ application

annotation 모듈 gradle 설정

동적으로 소스코드를 추가하기 위해 javapoet을 이용했다.

또한 autoservice도 함께 추가해줬다. autoservice는 아래와 같은 기능을 한다.

  • javax.annotation.processing.Processor 과같은 manifest 파일을 자동으로 생성

⇒ 컴파일 시점에 애노테이션 프로세서를 사용하여 javax.annotation.processing.Processor 파일을 자동으로 생성

plugins {
    id 'java'
}

group = 'org.example'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.squareup:javapoet:1.13.0'
    // <https://mvnrepository.com/artifact/com.google.auto.service/auto-service>
    implementation 'com.google.auto.service:auto-service:1.1.1'
    annotationProcessor 'com.google.auto.service:auto-service:1.1.1'

}

test {
    useJUnitPlatform()
}

PerformanceTest 어노테이션 생성

어노테이션 패키지 내에 PerformanceTest라는 이름의 어노테이션을 생성해줬다. 클래스를 대상으로 할 것이기 때문에, Target을 ElementType.TYPE으로 지정해줬다.

또한, 컴파일 이 후에는 어노테이션 정보가 필요없기 때문에 Retention을 RetentionPolicy.SOURCE로 지정해줬다.

package com.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface PerformanceTest {
}

어노테이션 처리부분

어노테이션 처리의 경우, 필드 값과 메서드를 그대로 가져오지만, 메서드에 한해 해당 메서드의 body 앞 뒤로 System.currentTimeMillis() 함수 실행부분을 넣고 마지막에 body를 실행하는데에 걸린 시간을 출력하도록 구성했다.

@AutoService(Processor.class)
public class PerformanceProcessor extends AbstractProcessor
{

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
    {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(PerformanceTest.class);
        List<FieldSpec> fieldSpecList = new ArrayList<>();
        List<MethodSpec> methodSpecList = new ArrayList<>();

        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
            "list all : " + elements.toString());
        for (Element element : elements)
        {

            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
                "CustomGetter process : " + element.getSimpleName());

            TypeElement typeElement = (TypeElement) element;

            for (Element field : typeElement.getEnclosedElements())
            {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
                    "field : " + field.getKind());

                if (field.getKind() == ElementKind.METHOD) {
                    ExecutableElement executableElement = (ExecutableElement) field;
                    Trees trees = Trees.instance(processingEnv);
                    String methodBody = trees.getTree(executableElement).getBody().toString();
                    methodBody = methodBody.substring(2, methodBody.length());
                    methodBody = methodBody.substring(0, methodBody.length()-3);
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
                        "element body : " + methodBody);

                    String methodNm = String.format("p%s", field.getSimpleName());

                    MethodSpec methodSpec = MethodSpec.methodBuilder(methodNm)
                        .addModifiers(Modifier.PUBLIC)
                        .addStatement("long start = System.currentTimeMillis()")
                        .addStatement(methodBody)
                        .addStatement("long end = System.currentTimeMillis()")
                        .addStatement("System.out.println(String.format(\\"method running time : %d\\", end-start))")
                        .build();

                    methodSpecList.add(methodSpec);

                } else if (field.getKind() == ElementKind.FIELD) {
                    String fieldNm = field.getSimpleName().toString();
                    TypeName fieldTypeName = TypeName.get(field.asType());

                    FieldSpec fieldSpec = FieldSpec.builder(fieldTypeName, fieldNm)
                        .build();

                    fieldSpecList.add(fieldSpec);
                }
            }
            ClassName className = ClassName.get(typeElement);
            String getterClassName = String.format("P%s", className.simpleName());

            TypeSpec getterClass = TypeSpec.classBuilder(getterClassName)
                .addModifiers(Modifier.PUBLIC)
                .addFields(fieldSpecList)
                .addMethods(methodSpecList)
                .build();

            try
            {
                JavaFile.builder(className.packageName(), getterClass)
                    .build()
                    .writeTo(processingEnv.getFiler());
            }
            catch (IOException e)
            {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "ERROR : " + e);
            }
        }

        return true;
    }
}

application 모듈 gradle 설정

annotation 모듈을 dependency에 추가해준다.

compileOnly를 통해 해당 모듈이 컴파일시에만 포함하도록 하고

annotationProcessor를 통해 어노테이션 처리를 할 수 있도록 해준다.

plugins {
    id 'java'
}

group = 'org.example'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    compileOnly project(':annotation')
    annotationProcessor project(':annotation')
}

test {
    useJUnitPlatform()
}

annotation 테스트

테스트용으로 클래스를 하나 생성해주고, 아까 생성한 PerformanceTest 어노테이션을 붙여준다.

import com.annotation.PerformanceTest;

@PerformanceTest
public class AnnotationTest {
    public String s;
    public int a;
    public void run() {

        for (int i = 0; i < 1000000000; i++) {
            a+=i;
        }
    }
}

이후 빌드를 하게 되면 아래와 같이 어노테이션 프로세서에서 생성했던 클래스가 추가된다.

이후 클래스를 생성 할 때에는 AnnotationProcessor에 의해 생성된 클래스로 접근하여 사용할 수 있다.

package com.main;

import java.lang.String;

public class PAnnotationTest {
  String s;

  int a;

  public void prun() {
    long start = System.currentTimeMillis();

            for (int i = 0; i < 1000000000; i++) {
                a += i;
            };
    long end = System.currentTimeMillis();
    System.out.println(String.format("method running time : %d", end-start));
  }
}

 

package com.main;

public class Main {

    public static void main(String[] args) {
        PAnnotationTest pAnnotationTest = new PAnnotationTest();
        pAnnotationTest.prun();
    }
}

정리

annotation은 소스코드를 동적으로 수정할 수 있는 만큼 잘 다룰 경우 개발에 있어 편리함을 줄 수 있을 것 같다. 지금보니 QueryDSL도 비슷한 방식으로 구현이 되어있는 것 같은데, 잘 활용할 수 있도록 좀 더 공부해두는 것이 좋을 것 같다.

전체 코드는 https://github.com/gogoadl/gradle-annotation-processor 에 업로드 되어 있습니다.

Reference

https://jeong-pro.tistory.com/234

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=vefe&logNo=222072718782

https://catsbi.oopy.io/78cee801-bb9c-44af-ad1f-dffc5a541101

https://stackoverflow.com/questions/74546841/get-method-source-code-on-annotation-processor

'Java' 카테고리의 다른 글

[자바] 빌더패턴이란?  (0) 2020.07.16

+ Recent posts