블로그

코드캠프

입문자도 가넝한 8주만에 개발자 되는 법

안녕하세요! 실무 코딩부트캠프, 코드캠프입니다 :)날씨가 조금씩 따뜻해지면서 식곤증이 부쩍 늘어나고 있지 않나요?(전지적 컴퓨터 시점)그래서 코드캠프가 눈이 번쩍! 뜨이는(👀) 강의 업데이트 소식을 준비해왔어요!특히 비전공이지만 개발 커리어에 관심이 있는 분들이라면, 주목해주셔도 좋아요 :)코딩을 몰라도(NEW!) 2개월만에 개발자가 되는 '관리형' 코딩 부트캠프인프런에 입성한지 얼마 되지 않았지만 뜨거운 관심과 애정 어린 피드백을 받으며, 더 나은 컨텐츠를 제공하기 위해 코드캠프도 열심히 성장 중인데요. (정말 감사합니다)그 중 사전 기초 지식 없이는 부트캠프 합류가 어려워 신청을 망설였던 분들을 많이 보았어요.그래서 상담 후 등록을 완료해주신 모든 분들께,[시작은 프리캠프], [강력한 CSS], [훈훈한 Javascript] 기초 강의를 무료로 제공합니다!사전 지식을 필요로 하시는 분들은 웹 개발의 기초를 습득하시고사전 지식이 이미 있으신 분들은 기초 내용을 복습하면서 튼튼하게 다져보세요 :)⚠️ 단, 기초 강의 수강 기간은 부트캠프 기간(8주)에 포함되지 않으므로 등록하신 기수의 개강 일정에 맞춰 수강해 주셔야 합니다!(MBTI가 P인 분들)개강 전 기초 강의 수강 계획을 짜기 힘드신 분들은 언제든 코드캠프에 문의해주세요!개강일에 맞춰 수강할 수 있도록 체계적인 시간표를 제공해드리겠습니다 😊👉 2기 일정 : 03.06 - 04.28 (선착순 모집) ▶ 20% 할인 중!👉 3기 일정 : 04.03 - 05.29 (선착순 모집) ▶ 20% 할인 중!

웹 개발웹개발프론트엔드백엔드부트캠프비전공자JavascriptNode.jsReactNext.jsNest.js

코드캠프

Github Copilot, 진짜 개발자 대체가 가능할까?

요즘 Github Copilot에 관해 여러가지 의견들이 충돌하며 의견이 분분하다는 것을 본 코캠.어떤분들은 '미래에 개발자는 Copilot이 대체할 거다' 라는 의견이 있는가 하는 반면, '치와와랑 머핀도 구분 못하는 AI가 어떻게 대체하냐,아직 미흡하다.' 라는 의견도 있었습니다.이런 토론은 개발자로서 굉장히 참을 수 없는 의견대립이죠.그래서 코캠측에서도 슬그머니 의견을 끼워 넣어보기 위해 Github Copilot에 대해 알아보았습니다. 🛠 Github Copoilot?깃허브 코파일럿은 내가 원하는 기능을 주석으로 묘사하면, 묘사에 맞는 기능을 자동으로 완성시켜주는 자동 코딩 시스템입니다.Copilot 빠르게 시작하기1️⃣ 작동방식1. 내가 원하는 기능을 주석으로 묘사합니다.2. 코파일럿 AI가 딥러닝 한 내용을 바탕으로 '대부분 이렇게 쓰던데?' 하는 코드들을 완성합니다. 2️⃣ Github Copoilot의 장단점[ 장점 ]웬만큼 연차가 쌓인 개발자가 아니고서야 라이브 코딩을 하는 개발자는 생각 보다 많이 없습니다.주니어 개발자의 대부분은 다른 사람들이 쓴 코드를 참고하고 긁어와 사용하는 경우가 더 많죠.이런 부분에 있어서는 코파일럿이 도움이 될 수 있습니다.[ 단점 ]코파일럿의 학습이 완벽하지 않기 때문에 개발자의 의도가 정확하게 컴퓨팅 사고를 기반으로 제시되지 않으면, AI는 갈 길을 잃고 의도와 다른 코드를 제시할 수 있습니다.개발자의 의도와 다른 코드는 결국 불필요한 코드를 늘리는 것과 같기 때문에 비효율적일 수 있습니다. 3️⃣ Copoilot의 궁극적인 문제?copoilot의 궁극적 문제는 라이선스 문제가 되지 않을까 싶습니다.코파일럿의 AI가 어떤 라이선스인가를 따지지 않고 학습하기 때문에 뱉어낸 결과물에 제한된 라이선스 코드가 있다면 해당 코파일럿 코드 또한 제한되어야 하는지, 적용한다면 어느 범주까지 적용해야 하는지 애매한 부분이 있다고 합니다.  ❓ 그래서 진짜 개발자 대체가 가능해?여기부터는 코드캠프 일부 개발자들의 의견으로 반박시 여러분들 의견이 맞습니다.😁코드캠프에게 여러분들의 의견을 알려주세요! 코캠측 개발자들의 의견을 정리해보았는데요, 생각보다 코캠 내부에서도 파가 나뉘었습니다!🧑🏻‍💻 백엔드 개발자들Captain( 팀 내 그저 빛을 맡고 계신 9년차 풀스택 개발자 )- 설계를 하는 시니어들은 대체 불가능, 단순 업무를 하는 주니어는 대체 가능.틀을 설계하는 건 인공지능이 발전해도 인간의 창의성까지 가지고 올 수 없는 부분이 있기 때문에 무리라고 생각.Quokka- 비슷한 거 써봤는데 대체 안됨.( 일단 돈을 안냈음. - 무료판 유저 )Otter- 회사의 도입이 대중화 되느냐에 따라 다를 것 같은데, 주니어는 대체 가능하지만 로직의 틀을 짜야 하는 시니어는 대체가 불가하다.Bommy- 상용화 시기가 중요하다고 생각, 과도기동안은 주니어도 대체가 안되지만 상용화 된 이후에는 주니어는 대체가 가능하다고 생각.🧑🏻‍💻 프론트엔드 개발자들Eunny- 주니어, 시니어 모두 대체가 불가하다.공부는 가능하겠지만, 모든 코드는 각자 코드 상황에 따라 다르게 적용되기 때문에 대체 불가.Hoony( 공상과학에 빠져있는 디지털노마드 선두주자 )- 주니어, 시니어 모두 대체가 가능하다. ( 터미네이터와 아이언맨 자비스를 너무 감명 깊게 봄. - AI가 인류를 대체할 수 있다 주의 )시간이 흐를 수록 데이터는 누적될 것이고 대부분의 코드 설계가 가능한 수준까지 올라 갈 수 있을 것, 이를 통해 인건비 절감을 위해 개발자 보다 AI를 선호하는 상황이 생길 수 있다.Jenny- 주니어 정도는 코파일럿으로 대체가 가능하다. 하지만 코파일럿 설계를 해야 하는 엔지니어나 서비스의 큰 틀을 짜야하는 시니어는 대체가 불가능하다. 큰 틀은 언제나 상황에 따라 짜야 하는데 통상적인 부분으로는 커버 불가능함.Gee- 코파일럿이 상용화되어서 많은 사람이 사용하게 된다면 나중에는 코파일럿이 작성한 코드가 트렌드를 반영한 정석 로직으로 여겨지는 날이 올 것 같음. 하지만 상황에 맞게 배치하고 개선하는 작업을 하는 개발자는 반드시 필요하다고 생각. ( 근데 코파일럿 되게 좋은데? ) 코드캠프에서도 총 3가지 의견으로 나뉘었는데요,1. 주니어만 대체가 가능하다.2. 주니어, 시니어 둘 다 대체 가능하다.3. 주니어, 시니어 둘 다 대체 불가능하다.이렇게 총 세가지 의견 중 가장 우세한 의견은 [ 주니어는 대체가 가능하나, 시니어는 불가능하다! ] 입니다. 코드 캠프의 개발자들(일부)은 위와 같이 생각하는데, 여러분들은 어떻게 생각하시나요?여러분들의 의견도 들려주세요!

웹 개발개발자개발자대체GithubCopilotAI기술토론프론트엔드백엔드웹개발시니어개발자주니어개발자

인프런 아셀

자바스크립트, 더 이상 고민하지 마세요!

웹 페이지부터 서버, 애플리케이션까지 뚝-딱만들 수 있는 만능 프로그래밍 언어, 자바스크립트!​하나라도 해당되신다면 꼭! (。•̀ᴗ-)✧✓ 개발자로의 취업을 앞두신 분✓ 코딩/웹 개발을 시작하는 분✓ 자바스크립트의 개념을 탄탄히 쌓고 싶은 분✓ 다양한 프로젝트로 실력을 늘리고 싶은 분> 딱 맞는 자바스크립트 강의 보러 가기 <한 눈에 보는 추천강의 PICK내가 찾던 '그' JS 강의 살펴보기(1) JS Best 강의(2) 왕초보를 위한 강의(3) 한 번에! 올인원 강의(4) 가볍게 시작하는 무료 강의​"제일 많은 수강생에게 인정받은 강의로 시작하고 싶어요"(1) 인프런이 자신있게 소개하는 최고의 JavaScript 강의제대로 파는 자바스크립트 (JavaScript)✓ 평점 4.9점✓ 수업 80개 (13시간 3분)✓ 수강생 2,142명가장 최신의 자바스크립트에 대해 배우고 싶다면? 학습을 위해 복붙 가능한 실습 명령어+코드까지코어 자바스크립트✓ 평점 4.8점✓ 수업 8개 (1시간 57분)✓ 수강생 3,868명2시간만에 JS 핵심을 빠르게 배우고 this, 콜백, 스코프 등에 대한 동작원리 학습하기시나브로 자바스크립트✓ 평점 5.0점✓ 수업 118개 (19시간 56분)✓ 수강생 426명자바스크립트의 동작원리를 '제대로' 배우고 how가 아닌 why에 집중하는 정석 강의인프런에서만 만날 수 있는 맞춤형 자바스크립트 강의!왕초보를 위한 강의, 올인원 강의,그리고 가볍게 시작하는 무료 강의까지더 많은 강의로 나에게 딱 맞게 시작할 수 있으니까더 이상 고민하지 마세요! ദ്ദി˶ˊᵕˋ˵)>> 내게 맞는 강의 보러 가기 <<

웹 개발자바스크립트JS코딩프로그래밍언어웹개발프론트엔드javascript프로젝트개발자무료강의

모두의연구소

코딩 테스트 대비까지 완벽한 백엔드 기초 가이드

위니브, 제주코딩베이스캠프 대표 이호준 대표가 말하는 백엔드 개발자가 되기 위한 첫 걸음부터 완벽한 코딩 테스트 대비까지! 국가에서 지원하는 많은 부트캠프가 있습니다. 여러분도 비교 분석을 해보셨을 것이고요. 만약 비교 분석을 하지 않으셨다면 비교 분석을 해보시길 권해드립니다. 이 글에서는 이 과정이 어떠한 부분에서 특별한지를 설명해 드리고자 합니다. 1. 실무 경험 많은 강사진부트캠프마다 한 강사가 처음부터 끝까지 끌고 가는 강의가 있고, 챕터마다 강사가 나뉘는 강의도 있습니다. 둘 다 장단점이 있습니다. 저희는 각 분야의 전문성은 해당 분야 근무와 실무 경험이 있으신 분이 가장 잘 알고 있다고 판단했기 때문에 후자를 선택했습니다.파이썬과 인공지능은 국민은행 등에서 데이터 분석을 하셨던 김진환 님이, 프론트엔드는 다음 포털 검색 FE 개발 등을 하셨던 한재현 님이, 인프라는 반도체 회사에서 IT 담당하셨던 김승주 님이, 백엔드는 위니브의 대표인 제가(이호준) 이끌고 있습니다. 모두 위니브라는 조직에 속해있어 유기적으로 커뮤니케이션하고 있습니다. 자신이 맡은 분야 강의가 끝나도 채팅방을 나가거나 사라지지 않고요.또한 실무 경험과 더불어 가장 큰 것은 강의 경험이라고 생각합니다. 모두 아래 보이는 다양한 콘텐츠 제작과 대학, 대기업 등에 강의 경험, 출판 경험이 있는 분들입니다.저희는 다양한 콘텐츠 제작(책과 강의 제작) 경험과 다수의 부트캠프 운영 경험이 있습니다. 다양한 곳에 콘텐츠를 공급하고 있지만, 그중에서도 인프런에서는 57개 강의, 7만여 명 수강생, 평점 4.8점을 달성하고 있어요. 제주코딩베이스캠프라는 Django 부트캠프를 제주에서 진행하고 있기도 합니다. 카카오와 함께하는 알고리즘 산책 등 다양한 프로그램 운영 경험도 가지고 있습니다. 유튜브 채널은 제주코딩베이스캠프라는 채널을 운영하고 있고요.이렇게 쌓인 노하우를 통해 여러분의 드라마틱한 성장을 돕겠습니다.  2. 개별 학습 욕구에 맞춘 학습 방식많은 학생이 모여 수업을 듣는 만큼 다양한 학습 경험치를 가지고 있습니다. 컴퓨터공학과를 졸업한 분, 이제 막 시작하신 분, 이미 다른 기업에서 실무를 하다 오신 분 등이요. 다양한 분들이 들어오는 만큼 학습 방법에서도 차이가 있습니다.이제 막 시작하신 분은 모든 수업에 집중해서 들을 수 있도록 구성이 되어 있고, 관련 학과를 졸업하신 분들은 부족한 부분만 학습할 수 있도록 강의자료와 월별 영상 강의가 나갑니다. 이미 실무를 하신 분들은 책 출판이나 오픈소스 프로젝트 등 다양한 커리어를 쌓을 수 있도록 구성되어 있습니다. 이밖에도 수준별 스터디 그룹 구성, 개발 실무진의 멘토링 등을 통하여 가능하면 여러분의 다양한 학습 욕구가 해소될 수 있도록 진행하고 있습니다.공통 교육은 줌을 통한 온라인 라이브로 진행이 되고, 디스코드를 통해 실시간 질문을 받습니다. 진도는 중위 진도로 조정해가며 나가고, 어렵거나 쉬우신 분들을 위해 공부할 수 있는 학습자료나 자신의 실력을 가늠할 수 있는 과제를 드립니다.강사와 수강생의 시간이 꼭 동기화 될 필요는 없습니다. 이미 해당 과목이나 당일 나가는 진도에 대해 충분한 이해를 하고 있다면 더 높은 수준으로 넘어갈 수 있도록 학습 자료를 제공합니다. 부족한 부분 2%를 채우러 오신 분 같은 경우 과제로 바로 넘어가거나 오픈소스나 책 출판 프로젝트에 좀 더 몰입하는 분도 있으십니다.다만 이 경우에도 필수 과제는 꼭 해주셔야 합니다. 선택 과제와 필수 과제가 주어지는데요. 이 과제를 통해 여러분이 해당 과정에 꼭 필요한 학습 개념을 이해하고 있는지 판단합니다. 3. 책 출판과 오픈소스 프로젝트여러분을 좀 더 밀도 있는 개발자로 성장 시키고, 이력서에 한 줄 넣어 취업의 험난한 허들을 넘을 수 있도록 캠프 외 프로젝트로 책 출판 프로젝트와 오픈소스 프로젝트를 진행합니다.책 출판 프로젝트는 항상 무료책으로만 출판을 하고 있으며 반기나 분기별로 출판하고 있습니다. 이러한 프로젝트는 여러분의 이력서에 오랫동안 남을 것입니다. 지금 취업뿐만 아니라 여러분이 다음에 이직을 하실 때도 많은 도움이 될 것이고요.다만 이 프로젝트는 자율 프로젝트입니다. 출판이 쉬운 작업은 아니기 때문에 의지가 있어야 합니다. 책 출판은 의지가 있는 분들을 모으고, 브레인스토밍을 통해 주제를 선정한 다음 그 주제를 집필할 분들을 모아 팀끼리 집필하게 됩니다. 필수로 참여해야 하는 프로그램은 아닙니다. 한 권의 책을 출판한다는 것은 쉬운 일은 아닙니다. 쉽지 않기 때문에 가치가 있습니다. 이를 통해 여러분들은 좀 더 밀도 있는 개발자가 되실 수 있습니다.오늘 자(2023/11/22) 리디북스 컴퓨터/IT 전체 무료 책 인기순으로 보았을 때 첫 페이지에 있는 책 대부분이 저희가 집필한 책이거나 캠프에 참여한 학생들이 집필한 책입니다. 출판사 사도출판으로 확인하시면 됩니다.오픈소스 프로젝트는 관련된 주제가 있을 때 진행하고, 관련된 주제가 없을 경우 진행하지 않습니다. 보통 반기에 한 건씩 진행하고 있습니다. 대표적인 프로젝트로 제주특별자치도 상황판으로도 사용했었던 라이브 코로나(https://livecorona.co.kr/)가 있고, 스탑워(https://stopwar.co.kr/), 플랙스엔그리드(https://flexngrid.com/), 에스큐엘스쿨(https://sqlschool.co.kr/) 등을 진행했었습니다. 오픈소스 프로젝트는 실무에 계신 분 중 뜻이 있는 분들이 합류해 함께 개발합니다. 4. 반복학습과 코드 리뷰, 코딩 테스트처음 하는 분에게 가장 두렵고 힘든 것은 ‘익숙하지 않음’ 입니다. 12번의 반복을 통해 Django를 학습할 수 있도록 다양한 프로젝트가 준비되어 있고, 실무에 대한 막연한 두려움도 이겨내실 수 있도록 실제 실무에서 하는 것과 유사한 프로젝트가 준비되어 있습니다.각 프로젝트에는 발표회가 준비되어 있습니다. 이 발표회와 프로젝트 시트(sheet)를 통해 서로가 서로의 프로젝트를 공유할 수 있는 구성으로 동반성장 할 수 있도록 구성이 되어 있습니다. 발표마다 상장이 준비되어 있습니다. 발표가 있는 프로젝트는 총 3개이고 별개로 대상, 최우수상, 우수상이 나가게 됩니다.이러한 경험과 과제가 쌓여갈 때마다 할 수 있다는 자신감이 생기실 것입니다. 쉬우니까 할 수 있다는 자신감이 아니고 어렵지만 할 수 있다는 자신감입니다. 모두 구현하지 못하더라도, 일부라도 발표를 할 수 있도록 합니다. 부담감도 성장의 동력으로 쓸 수 있도록 여러분을 이끌겠습니다.또한 막연하게 여러분에게 어떤 결과물을 요구하는 것이 아니라 명확한 가이드를 통해 여러분이 갖춰야 될 구성 요소에 대해 예시를 통해 말씀드립니다. 예를 들어 발표는 대부분 Readme 파일로 진행을 하는데요. 아래 샘플 레포를 통해 어떤 식으로 구성하는지 감을 잡을 수 있습니다.링크 : https://github.com/weniv/project_sample_repo과제 발표와 동시에 코드 리뷰를 진행합니다. 실제 실무에서 받는 코드 리뷰 절차 등에 대해서도 설명해드리고 어떤 코드가 좋은 코드인지에 대해서도 여러 사례를 기반으로 얘기해드립니다. 코딩 테스트는 현재 출제되는 문제의 유형 분석, 문제의 출제 빈도, 기업별로 분석해둔 코딩 테스트 유형 등 다양한 데이터 기반 자료를 토대로 여러분들에게 명확한 가이드와 전략을 드리도록 하겠습니다.위니브에서는 그동안 여러 권의 알고리즘 책을 출판했으며, 제주 알고리즘 베이스캠프(https://jejualcam.co.kr/), 카카오와 함께하는 알고리즘 산책 등 다양한 행사를 진행해 왔습니다. 또한 알고리즘 테스트 서비스(https://pyalgo.co.kr/)도 운영하고 있는데요. 이러한 경험을 기반으로 여러분이 알고리즘에 보다 쉽게 다가갈 수 있도록 돕겠습니다.다만 코딩 테스트는 아쉽게도 왕도가 없습니다. 여러분이 매일매일 훈련하셔야 합니다.  4. 이력서 템플릿 제공과 리뷰, 코딩 테스트 대비저희가 한 해에 리뷰하는 이력서는 500건에서 700건 정도 됩니다. 이력서 검토 경험을 ‘신입개발자 이력서 작성 가이드’라는 책으로 집필하기도 했습니다.리뷰를 했던 데이터를 기반으로 희망 연봉에 따라 이력서를 검토해드립니다. 또한 희망 연봉에 따라 준비해야 하는 요건이 다를 수 있습니다. 예를 들어 고액 연봉을 희망한다면 코딩 테스트를 준비해야 하지만 연봉 3200 미만에서는 코딩 테스트가 거의 없습니다. 전략적으로 어떤 포지션을 취해야 하는지도 함께 알려드립니다.이력서를 노션으로 많이 작성하는데요. 실무자가 어떤 것을 선호하는지 모르기 때문에 다양한 양식을 준비해둘 필요가 있습니다. 생각보다 Notion 이력서를 좋아하지 않는 곳도 많습니다. 저희는 PDF양식과 노션 양식을 모두 제공해드립니다. PDF 양식은 아래 링크에서 무료로 확인하실 수 있습니다.https://paullabworkspace.notion.site/Figma-bfa32213fc244db9b31bb8486a479ee6?pvs=4 5. ICT 교육에 대한 치열한 고민캠프를 진행하게 되면 우리끼리 간혹 하는 얘기가 있습니다. ‘이 교육은 우리밖에 못한다’라는 얘기인데요. 이유는 우리는 ICT 교육에 대해, 한 과목 한 과목에 대해 치열하게 파고들고 회의하여 적재적소에 학습요소를 배치할 수 있는 그룹이기 때문입니다. 또한 단순히 교육의 퀄리티를 고민하는 것이 아니라 교육을 넘어 여러분에 이력에 무엇이 들어가야 좋을지, 이력서는 어떻게 써야 하는지 실질적인 컨설팅을 병행할 수 있는 그룹이기 때문에 그렇습니다. 우리는 여러분의 취업과 성장에 진심인 그룹입니다.우리의 방식이 모든 사람에게 맞는다는 생각은 가지고 있지 않습니다. 비교해보시고, 분석해보시고 선택해주시길 바랍니다.

백엔드백엔드웹개발파이썬장고PythonDjangoAI서비스인공지능머신러닝프레임워크

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 4주차 발자국 - 인스타그램 클론코딩

이번주는 강의 볼륨이 제일 많았던 인스타그램 클론코딩을 진행했다. 로그인, 회원가입을 위한 Supabase Auth와 실시간 채팅을 구현하기 위한 Supabase Realtime Database 배포까지 구현해볼 수 있었다. 이번시간엔 거의 다른 주차에 비해 2배정도 되는? 양인거 같아 월요일부터 틈틈히 해서 겨우 시간을 맞출 수 있었다. 수강 내용Section 6 인스타그램 클론코딩 - Supabase 인증 구현 Part 1이번 챕터에서는 Supabase 인증 시스템을 구현해보는 것이었다. 다음과 같은 기능을 구현했다.이메일 인증을 통한 회원가입OTP 인증을 통한 회원가입로그인(추가) 카카오 소셜 로그인이는 Supabase Auth를 통해 구현했다. 강의에서는 위의 내용들만 구현했지만 Supabase Auth에는 더 다양한 기능들을 제공하고있었다. JWTSession 등을 지원하기도했고, 권한관리 등도 제공했다.그리고 강의를 들으면서 느꼈던 것이지만 React Query를 잘 사용하셔서 React Query의 활용성에 대해서도 한번 더 성장한다는 느낌을 받았다.Section 7 인스타그램 클론코딩 - 인스타 DM 채팅 기능 구현 Part 2인증 기능을 모두 마친 뒤에는 실시간 채팅 기능을 구현했다. 이 시간에 Supabase Realtime Database를 학습할 수 있었다. 사실 그냥 Database랑 어떤 차이인지는.. 잘 모르겠지만 해당 기능을 켰을 때 실시간으로 채팅 기능을 수현할 수 있었다. 이때에도 React Query를 이용해 데이터를 캐싱하고 다시 불러오는 작업을 함으로써 좀 더 효율적으로 Supabase Table 데이터들을 관리할 수 있었다. 그리고 전에도 얘기했던것이지만 Database라서 그런지 메서드 자체가 SQL문을 그대로 가져와서 학부시절 DB를 배워놨었던 부분이 이해하기도, 적응하기에도 좀 더 편했었다.export async function getAllMessages({ chatUserId }: { chatUserId: string }) { const supabase = await createServerSupabaseAdminClient(); const { data, error } = await supabase.auth.getSession(); if (error && !data?.session) { throw new Error("User is not authenticated"); } const { data: messages, error: messagesError } = await supabase .from("message") .select("*") .or(`receiver.eq.${chatUserId},receiver.eq.${data?.session?.user.id}`) .or(`sender.eq.${chatUserId},sender.eq.${data?.session?.user.id}`) .order("created_at", { ascending: true }); if (messagesError) { throw new Error("Failed to get messages"); } return messages; } const { data: messages, error: messagesError, isLoading: messagesLoading, refetch, } = useQuery({ queryKey: ["messages", selectedIndexState], queryFn: async () => { const allMessages = await getAllMessages({ chatUserId: selectedIndexState, }); return allMessages; }, });useEffect(() => { const channel = supabase .channel("message_postgres_changes") .on( "postgres_changes", { event: "INSERT", schema: "public", table: "message", }, (payload) => { console.log(payload); } ) .subscribe(); return () => { channel.unsubscribe(); }; }, []);그리고 메시지를 주고받고 한 다음 채팅을 그냥 보여주기만 하면 실시간으로 동기화가 안된다는 문제점이 있어 (새로고침을 해야 보여진다.) 그 때를 위한 동기화 작업을 supabase channel기능을 사용했다.Section 8 웹사이트 배포하기 - 도메인 등록, Vercel, AWS 배포Vercel 배포같은 경우는 개인적으로 포트폴리오 배포할 때 유용하게 사용했었고 AWS배포도 한번쯤은 해보는것이 좋다고 해서 해본적이 있어서 좀 가볍게 들었다. 그리고 배포 시 빌드 에러를 최소화하기 위해 항상 push하기 전에 build한번 하고 push를 해서 빌드 오류도 없이 무난하게 모든 프로젝트를 배포할 수 있었다.미션4주차 미션은 다음과 같았다.작성한 모든 프로젝트 배포하기채팅 메시지 삭제 기능 구현채팅 읽음, 안읽음 표시 기능 구현삭제기능, 읽음/안읽음 표시 기능을 위해 supabase data table에 is_readis_deleted 항목을 추가하고 각 항목들을 update하는식으로 구현했다. 그리고 동기화를 위해 channel에 update 항목을 추가해주는걸로. 인터넷에서 찾아보니 *을 이요해 모든 이벤트 INSERTDELETEUPDATE 등을 포함할 수 있다고 했지만 그냥 업데이트만 하나 추가해주었다. ui는 그렇다 치고.. 일단 삭제 기능이랑 읽음 기능까지는 구현해봤다.마무리인스타그램 클론코딩을 끝으로 이렇게 4주간의 대장정이 마무리가됬다. Supabase 프로젝트 한번 해보겠다고 들을까 말까 했던 강의였는데 이렇게 다같이 스터디할 수 있는 기회가 생겨서 나 혼자 했으면 흐지부지 되었을텐데 이렇게 끝까지 완강할 수 있어서 개인적으로 뜻깊었던 스터디 기간이었다.강의 내용도 부실하지 않고 적당히 그리고 자세하게 알려주셔서 내용을 이해하기도 쉬웠다. 또한 Supabase 자체에 국한되지 않고 Next.js나 React Query같은 프론트엔드 지식도 함께 쌓아 더 괜찮은 시간이었다. 지금 이 토대를 기반으로 다음에 할 Supabase를 통한 개인 프로젝트도 화이팅해야겠다!

웹 개발웹개발프론트엔드백엔드supabase

희주

[인프런 워밍업 클럽 3기 풀스택] 4주차 발자국

학습 내용마지막 주에는 인스타그램 프로젝트 클론코딩과 Vercel, AWS EC2 배포, 도메인 등록 및 연결까지 진행했다. Supabase Auth를 활용해 인증을 다루고, Supabase Realtime에 대해 알아보고 실시간 DM 채팅을 구현했다.Supabase Auth를 이용해 인증방식 기획, 회원가입/로그인/로그아웃 기능 구현회원가입 방식: Confirmation URL / 6-Digit OTP로그인 방식: Email, password카카오 소셜 로그인/회원가입SMTP 서비스랜덤 이미지 API로 유저 프로필 이미지 적용Supabase Realtime으로 채팅 구현 Postgres Changes로 DB 상태 변경 실시간 추적 - message INSERT 이벤트 구독, 실시간 채팅 구현Presence로 유저 실시간 추적 - 온라인 유저 파악, 유저별 마지막 접속시간 표시Supabase RLS(Row Level Security) 설정 - 테이블 접근 권한 설정, Client-Side에서 Supabase 접근Vercel, AWS EC2로 프로젝트 배포도메인 구매 및 등록(Vercel과 연결하기) 미션이제까지 만드신 모든 프로젝트를 배포하신 후 배포된 링크를 업로드 해주세요.강의 내용대로 배포를 진행하니 Vercel로 모두 배포할 수 있었다. Vercel은 Next.js 팀이 만든 플랫폼이라 Next.js 프로젝트를 배포하기에 수월하겠다는 생각이 들었다. 지금까지의 프로젝트는 모두 Github에 올려놓았기에, 이를 연동해 배포할 리포지토리를 고르고 Import를 눌러 환경변수를 입력한 뒤 Deploy하면 끝이었다. Deploy에 앞서 프로젝트 build에 문제가 없는지 체크한 후 진행했다.모두 배포를 완료한 뒤 인스타그램 프로젝트에서 카카오 로그인을 시도하니 리다이렉트 문제를 겪어 처음에 당황했는데 강의 질문 답변을 보고 Supabase Authentication에서 Redirect URLs에 배포 URL를 추가하니 해결되었다. 마무리예상대로 마지막 4주차 내용이 가장 어려워 따라가기 쉽지 않았다. 이전까지는 어떻게든 이해가 됐었는데, Supabase Realtime의 Presence를 이용하는 부분은 처음에 도저히 이해되질 않아서 일단 똑같이 따라만 했던 기억이 난다. 그래도 이렇게 복잡할 수 있는 부분을 쉽게 따라가며 습득할 수 있다는 것에 감사했다😌짧은 시간 동안 다양한 핵심 기술과 유용한 기능 구현도 빠르게 터득할 수 있었고, 덕분에 구현에 대한 자신감도 조금씩 늘어 의미 있는 시간이었던 것 같다. 또 예전에 AWS EC2를 이용해 겨우 배포해보고 배포는 너무 어려운 것이라 느끼고 흥미를 잃었었는데 이번에 전체적인 EC2 배포 과정도 알아가고, Vercel로 프로젝트가 정말 쉽게 배포되는 걸 보면서 ‘이제 뭐든 시도하고 만들어 올려볼 수 있겠다’는 생각이 들었다!개인적으로는 배포만이라도 하는 것이 목표였기에…ㅎㅎ 일단 완주했다는 것에 만족하고 있다. 자유자재로 코드를 바꾸지 못할 때, 발견한 문제를 해결하고 싶어도 방법을 모를 때😂 스스로의 부족함에 고민도 많았던 것 같다. 그럼에도 포기하지 않고 여기까지 올 수 있었던 건 워밍업 클럽에 참여하고 강의를 진행하며 느낀 성취 덕분이라고 생각한다.가끔씩 처음 코딩을 시작했을 때를 떠올리며 몰랐던 성장을 체감하곤 했는데, 이번 워밍업 클럽도 마찬가지로 나중에 돌이켜보면 한 달 동안 내 생각보다 많은 것을 배우고 해냈다고 느끼리라는 생각이 들었다. 이번 학습이 헛되지 않도록 배운 것을 활용한 간단한 프로젝트부터 만들어보고 전진할 생각이다.🙂 많은 도움 되는 시간 만들어주신 강사님과 인프런에 감사드리고, 함께 워밍업에 참여한 분들도 진심 대단하세요! 모두 응원합니다!

풀스택풀스택웹개발Next.jsSupabase

희주

[인프런 워밍업 클럽 3기 풀스택] 3주차 발자국

학습 내용이번 주에는 Netflix 프로젝트 클론코딩을 진행했다. 영화 목록 페이지 및 검색 기능과 개별 영화 상세페이지를 구현했다.supabase에서 영화 테이블을 만들고, 준비된 csv 파일로 바로 영화 데이터를 추가header에서 입력한 검색어를 다른 컴포넌트에서 사용하기 위해 Recoil 사용dynamic routing으로 개별 영화 상세페이지 구현영화 목록을 보여줄 때 무한스크롤 적용react-intersection-observer 라이브러리를 사용해, 보이지 않는 태그를 심어놓고 감지react-query의 useQuery 대신 무한스크롤을 쉽게 구현할 수 있는 useInfiniteQuery 이용상세페이지의 동적 meta tag 생성을 위해 generateMetadata() 사용해 SEO 작업 미션Netflix Clone 프로젝트에 “찜하기” 기능을 추가하세요.공용 즐겨찾기 기능을 구현하고, 찜한 영화를 리스트 최상단에 보이도록 정렬했다. 찜한 영화 리스트를 가져와서 별도의 찜 목록 페이지에서 보여주는 방법도 고려해볼 수 있을 것 같다.movie 테이블에 bool 타입의 bookmarked column을 추가하고 초기값은 FALSE로 설정searchMovies()에서 order()로 정렬 기준을 추가해 찜한 영화부터 보이도록 하고 다중 정렬하여 찜한 영화 중에서도 id 순서대로 정렬되도록 작성 const { data, error } = await supabase .from("movie") .select("*") .like("title", `%${search}%`) .order("bookmarked", { ascending: false }) .order("id", { ascending: true }) .range((page - 1) * pageSize, page * pageSize - 1); 북마크 버튼을 누르면 북마크 상태가 반대로 바뀔 수 있도록 Server Action 작성export async function updateBookmark(id, status) { const supabase = await createServerSupabaseClient(); const { error } = await supabase .from("movie") .update({ bookmarked: !status }) // 현재 상태 반대로 변경 .eq("id", id); handleError(error); return !status; } movie-card에서 북마크 mutation을 작성해 updateBookmark()를 실행하고 성공하면 화면이 바로 업데이트되도록 함const updateBookmarkMutation = useMutation({ mutationFn: () => updateBookmark(movie.id, movie.bookmarked), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["movie"] }); }, }); bookmarked 값에 따라 북마크 icon을 다르게 표시하고,북마크 클릭 시 mutation을 실행해 해당 영화의 bookmarked 값을 반대로 바꿔줌return ( ... {/* Bookmark 부분 */} <i onClick={() => updateBookmarkMutation.mutate()} className={`text-yellow-600 drop-shadow-md absolute flex items-center justify-center p-2 text-2xl top-2 right-2 z-20 ${ movie.bookmarked ? "fa-solid fa-bookmark" : "fa-regular fa-bookmark" }`} ></i> ... )  마무리이번 주에 배운 기능 중에는 이미 한두 번 다뤄본 내용들도 있었는데, 특히 useInfiniteQuery를 이용해 무한스크롤을 더 편리하게 적용해볼 수 있었던 것 같다(그래도 어려웠다…😅). 중간에 막히는 부분도 있었지만, 자세한 사용법을 찾아보고 강의를 따라가며 무한스크롤 구현 과정을 되짚어볼 수 있었다. Next.js가 알아서 해주는 동적 메타데이터 생성으로 간편하게 SEO 적용하는 방법도 알아갈 수 있었다. 현재 아쉬운 점 중 하나는 찜하기 기능을 적용했을 때 비교적 아래쪽의 영화를 찜하니 모든 영화 데이터를 다시 요청해오느라 북마크 표시가 늦게 반영된다는 것인데, 이번 경험으로 invalidateQueries() 대신 setQueryData() 활용을 고려하는 등 네트워크 요청 최적화의 필요성을 느꼈다. 마지막으로 다음 주가 가장 어려운 부분이 될 것 같지만, 끝까지 완주하는 것을 목표로 최선을 다해보려 한다!

풀스택풀스택웹개발Next.jsSupabase

Yang HyeonBin

[인프런 워밍업 클럽 3기] 풀스택 과정 3주차 발자국 👣

어느새 3주차다!느낀점은, 혼자 강의를 신청했으면 절대 이만큼 못왔을 거란 거다. 스터디에 참여했기에 강제성이 있어 이만큼 올 수 있었다.일하면서 강의를 듣는다는게 시간도 체력도 많이 필요한 일임을 느꼈다.다행인 점은 과제가 그렇게 많이 어렵진 않아서 다행히도 주말만 투자해서도 해낼 수 있다는 것..!마지막 주는 좀 더 빡셀지 모르겠으나,, 이번 주 공부한 내용을 정리해본다. 깃허브 링크 결과물 이미지! 이슈 해결1. 초기 세팅 후 npm install, npm run dev 다시 하기tailwind 같은 라이브러리는 설치 후 서버 다시 런해야 적용될 때가 있으니 스타일 적용이 안된다 싶으면 npm run dev 다시 해주는 걸 잊지 말자.2. Recoil과 최신 React 버전 충돌 문제Error: Cannot destructure property 'ReactCurrentDispatcher' of 'react__WEBPACK_IMPORTED_MODULE_0___default(...).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' as it is undefined.Recoil의 현재 버전은 0.7.7에 멈춰있음React 19, 18과 호환이 안되는 것으로 보임Zustand를 사용할까 했는데, Next.js의 속성과 충돌하는 면이 있다고 함아래의 인용 참고 (원문)in Next.jsNext.js 환경에서는 위 장점이 온전히 발휘되지 못한다. 공식적으로 Context 안에서 스토어를 초기화하고 저장하는 것을 권장한다. 때문에 기존 스토어 생성 -> 사용의 두 단계에서 Context 생성 -> 스토어 생성 -> 스토어 초기화 -> Provider 등록 -> 사용의 다섯 단계로 과정이 늘어난다.예시 코드는 공식 문서에서 볼 수 있다.이렇게 세팅의 차이가 나는 이유는 Zustand의 스토어가 전역 변수 기반이기 때문이다. Next.js는 서버와 클라이언트, 두 가지 환경에서 모두 실행되므로 전역 변수의 동작이 Zustand의 가정과 다르다.서버 컴포넌트, 클라이언트 컴포넌트 나뉘는 것이 원인인 듯함추가로, 가끔 발생하던 Hydration 에러를 좀 더 이해하게 됐는데, 서버와 클라이언트 컴포넌트 간에 공유되는 상태의 값이 서로 다를 때 발생하는 에러라고 함ssr과 hydration 관련 zustand 문서그래서, 궁금하기도 했던 Context API를 활용해보기로 함3. 무한 스크롤 - 새 데이터 불러오고 스크롤 위치 초기화되는 문제원인: 데이터 패치 중인 상태에 리턴할 컴포넌트를 먼저 배치하고, 그 아래 리스트 컴포넌트를 배치 → 새 데이터를 불러온 뒤 리스트 컴포넌트가 다시 렌더링되게 하는 듯if (isFetching || isFetchingNextPage) { return ( <div className="absolute top-0 bottom-0 left-0 right-0 flex items-center justify-center"> <i className="fa-solid fa-spinner animate-spin text-3xl"></i> </div> ); } return ( <div className="w-full h-full grid md:grid-cols-4 grid-cols-3 gap-1"> {/* 배열(전체 무비 데이터) 속 배열(페이지별 무비 데이터)이므로, 평탄화 */} {data?.pages?.map((page) => page.data ?.flat() ?.map((movie) => <MovieCard key={movie.id} movie={movie} />) )} {/* IntersectionObserver - 무한스크롤 구현을 위해 */} <div ref={ref} className="h-10"></div> </div> ); 해결: 순서를 바꿈return ( <div className="w-full h-full relative"> {/* 로딩 인디케이터 */} {isFetching && !isFetchingNextPage && ( <div className="fixed inset-0 flex items-center justify-center z-10"> <i className="fa-solid fa-spinner animate-spin text-3xl"></i> </div> )} {data && ( <div className="w-full h-full grid md:grid-cols-4 grid-cols-3 gap-1"> {/* 배열(전체 무비 데이터) 속 배열(페이지별 무비 데이터)이므로, 평탄화 */} {data?.pages?.map((page, pageIndex) => page.data ?.flat() ?.map((movie) => ( <MovieCard key={`page-${pageIndex}-movie-${movie.id}`} movie={movie} /> )) )} {/* IntersectionObserver - 무한스크롤 구현을 위해 */} <div ref={ref} className="h-10 col-span-full flex justify-center items-center"> {isFetchingNextPage && ( <i className="fa-solid fa-spinner animate-spin text-xl"></i> )} </div> </div> )} </div> ); 4. 검색 - 대소문자 구분 없이 검색 및 로딩 인디케이터 제거대소문자 구분 없이 검색 - ilike를 사용.ilike("title", `%${search.trim().replace(/\\s+/g, "%")}%`) // 대소문자 구분 X 로딩 인디케이터 관련: 검색 시 무한 스크롤 코드에서 계속 hasNextPage가 true로 되어 데이터를 무한으로 호출하는 버그였음hasNextPage 값을 처리하는 코드 개선 - 데이터 패치 필요없는 경우들에 명확히 undefined를 반환해 더이상 데이터 불러오지 않게끔 개선// useInfiniteQuery 사용하는 코드 getNextPageParam: (lastPage) => { // lastPage?.page ? lastPage.page + 1 : null, // 검색어 입력 시 무한으로 다시 호출하는 버그 // 데이터가 없으면 undefined를 반환하여 hasNextPage를 false로 설정 if (!lastPage.data || lastPage.data.length === 0) { return undefined; } // 마지막 페이지의 데이터 개수가 pageSize보다 작으면 undefined를 반환 if (lastPage.data.length < pageSize) { return undefined; } return lastPage.page + 1; }, 새로운 정보1. 무한스크롤 구현1) React-Intersection-Observer 라이브러리 (useInView 훅)화면에 요소가 보이게 됐는지 확인할 때 사용ref를 전달해 어떤 요소를 감지할지 명시,무한스크롤 구현에 사용보여줄 데이터 제일 밑에 보이지 않는 태그를 추가이 태그가 화면에 보이는 순간 다음 페이지 데이터를 가져오는 함수 호출‘이 태그가 보이는 순간’을 감지하는 데 useInView 사용순간을 감지하면 실행하라,는 코드는 useEffect 로 구현2) React-Query의 useInfiniteQueryuseQuery와 유사한데, fetchNextPage, hasNextPage, isFetchingNextPage라는 특수한 props 가짐2. Next.js - 페이지별 메타데이터 생성: generateMetadata영화 리스트 - 영화 상세 페이지로 이동했을 때 그 영화 관련 정보로 그 페이지의 메타데이터를 구성하고 싶을 경우 등에 사용ogImage: 해당 페이지 링크 공유 시 뜨는 이미지// movies/[id]/page.tsx 상단에 정의 // 각 영화 페이지에 맞는 동적인 메타데이터를 생성 export async function generateMetadata({params}: { params: { id: string }; }) { // 사용할 데이터 패치 const movie = await getMovieById({ movieId: Number(params.id), }); return { title: movie?.title || "", description: movie?.overview || "", // 메타데이터를 위한 이미지 URL - og이미지. 사이트 url 공유 시 보이는 이미지임 openGraph: { images: [movie?.image_url || ""], }, }; } export default function MovieDetail({ // 후략... 미션Notflix Clone 프로젝트에 "찜하기" 기능을 추가하세요. • 사용자가 특정 영화를 "찜"할 수 있도록 Supabase를 활용해 즐겨찾기 리스트 구현 찜한 영화를 영화 리스트 화면의 최상단으로 보여주도록 정렬찜하기 기능 추가 - movie 테이블에 favorited row 추가, boolean 타입으로최상단 보여주기 - 데이터 조회 코드를 수정찜한 데이터를 먼저 불러오고, 그외의 데이터를 불러오도록 수정?1. 찜하기 update 처리 후 movies 리스트 순서가 바뀌는 문제쿼리문의 order 기준을 정해 항상 같은 순서로 데이터를 불러오게 함release_date가 같은 경우 존재, 고유 값인 id를 보조 정렬 규칙으로 사용.order("release_date", { ascending: false }) .order("id", { ascending: true }) // 고유한 ID를 보조 정렬 키로 추가 2. 찜한 데이터 리스트 따로 조회 및 보여주기최근에 찜한 순서로 보여주는 게 일반적이라 판단, favorited → favorited_date (timestampz)로 변경조회하는 코드 분리 및 따로 리스트 생성: 찜한 컨텐츠는 Row, 가로 스크롤 배치

웹 개발웹개발Next.jsSupabase

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 3주차 발자국 - Netflix 클론코딩

이번주는 Netflix 클론코딩에 관한 내용이었다. Netflix 클론코딩이라던지 무한스크롤 구현이라던지 하는 내용은 이전에도 다른 강의에서 자주 나왔던 항목이어서 그런지 들을 때 좀 가볍게 들었던 것 같다. 다음 주에 있는 인스타그램 클론코딩의 강의+과제 볼륨이 거의 2주치가 되어서 이번주는 쉬어가는 듯 가볍게 듣고 다음 챕터 강의에 집중하려했다.이번 강의의 핵심은 supabase table에서 데이터 가져오기, 무한 스크롤 구현, SEO 최적화하기였다. 수강 내용Section 5. Netflix 클론코딩 - 영화검색 서비스 제작하기이번 챕터에선 Netflix 클론코딩을했다. 어느 때와 마찬가지로 tmdb에서 데이터를 가져와 영화를 뿌려주고, 해당 영화에 대한 정보를 보여주는 것이었다. 기술 스택은 지난번과 마찬가지로 Next.js, tailwind css를 기반으로했고 이번 프로젝트에서는 특히 상태관리가 필요해 zustand 라이브러리를 선택했다. 강의에서는 recoil을 사용했지만 리코일의경우 Next.js 15버전과 호환이 잘 되지 않기도 하고 평소에 가벼운 zustand를 자주 사용해서 해당 라이브러리로 상태관리를 진행했다.영화 목록 전체 불러오기우선 영화 데이터는 강의에서 준비해서 supabase 테이블에 모두 넣고 해당 테이블의 데이터들을 모두 불러오는 코드를 작성했다. const { data, error } = await supabase .from("movie") .select("*")검색 기능 추가하기위에서 말한 상태관리 라이브러리는 이 검색 기능을 활용하기 위해 사용한다. Header 컴포넌트에 있는 SearchInput 에서 검색을 하면 다른 곳에서도 해당 검색어를 사용하기 위해 검색어를 전역 변수로 지정했다.const { data, error } = await supabase .from("movie") .select("*") .like("title", `%${search}%`)search 키워드를 받아와 supabase table 내 title 컬럼에서 search 키워드가 포함된 항목들을 검색한다. 보면서 느끼는건데 확실히 SQL을 알고있으면 이런 키워드를 이해하는 데 좀 더 쉬운거같단 생각이 든다. table이라 그런지 다 SQL문법 사용하네.. 아무튼 이렇게 하면 supabase table에서도 검색어를 손쉽게 찾을 수 있다.무한 스크롤 구현무한 스크롤 구현하는 방법은 매우 다양하다. intersection observer을 사용한다던지.. 이번 강의에서는 가볍게 tanstack-query와 react-intersection-observer 라이브러리를 통해 무한 스크롤을 구현했다.const { ref, inView } = useInView({ threshold: 0 });위는 react-intersection-observer에서 사용하는 hooks이다.ref : 참조할 요소를 지정한다. inView : 요소를 불러와야 할 경우를 true false로 판별한다.threshold : 얼만큼 겹쳤을 경우 inView를 변경할 지 설정한다.이를 통해 데이터를 불러오는 곳 하단에 <div ref={ref} /> 을 작성하면 하단에 닿았을 경우의 트리거가 완성된다. 그리고 그 다음에는 tanstack-query에 있는 useInfiniteQuery를 사용한다.const { data, isFetchingNextPage, isFetching, hasNextPage, fetchNextPage } = useInfiniteQuery({ initialPageParam: 1, queryKey: ["movie", keyword], queryFn: ({ pageParam }) => searchMovies({ search: keyword, page: pageParam, pageSize: 12 }), getNextPageParam: (lastPage, allPages) => { return lastPage.page ? lastPage.page + 1 : undefined; }, });대충 이런식으로.. useQuery랑은 비슷하지만 hasNextPage, fetNextPage등 무한 스크롤 구현에 유용한 기능들이 포함되어있다. 강의를 들으면 깔끔하게 무한 스크롤까지 구현이 가능해진다.SEO (Next.js generateMetadata)Next.js에서는 동적으로 metadata를 생성해주는 기능을 제공한다. dynamic page같은 경우 각 페이지별로 메타데이터를 설정해주려고 하면 예를들어 id를 1, 2, 3 이렇게 다 따로 만들 수 없으니 이 때 generateMetadata를 사용하면 된다.export async function generateMetadata({ params }: any) { // Next.js에서는 params를 await 해야 함 const { id } = await params; const movie = await getMovie(Number(id)); return { title: movie?.title, description: movie?.overview, openGraph: { images: [movie?.image_url], }, }; }강의에서는 Next.js 14 버전이라 별 문제가 없었지만 나는 Next.js 15 버전을 사용해서 params를 가져올 때 async-await을 사용해서 가져왔다. 15버전에서 그냥 가져오면 동작은 하지만 에러가 발생한다. 이렇게까지 하면 가볍게 Netflix 클론코딩은 클리어. 미션3주차미션은 "찜하기" 기능을 구현하는 것이다. 유저 정보도 아직 없고 어떻게 구현할까 하다가 역시 로컬에 저장하는건 localstorage가 답이다 생각했다. 하지만 Next.js 는 SSR이라 localstorage를 그저 React처럼 사용한다면 기능이 정상적으로 동작하지 않는다. 따라서 zustand의 persist를 통해 로컬스토리지에 데이터를 쉽게 사용할 수 있도록 구현했다.마무리 이번주도 주말까지 무사히 일정을 잘 맞췄다. 몇번 했던 기술들이었지만 한번 더 복습 겸 꼼꼼히 들었다. 예전에는 javascript api 중 intersection observer을 이용해 무한스크롤을 깡으로 구현했었는데 확실히 라이브러리를 통해 구현하니까 많이 간편했다. 다음 인스타 클론코딩은 거의 2주치 분량이던데 다음주는 진짜 미리미리 듣고 추가미션까지 해낼 수 있도록 노력해야겠다. 마지막 4주차도 화이팅!

웹 개발웹개발프론트엔드풀스택supabase

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 2주차 발자국 - Dropbox 구현

이번주에 배운 내용은 Storage를 통해 Image를 CRUD 하는 기능이다. 이 강의를 들은 다음 추후 Next.js + Supabase 스택으로 블로그를 만들 생각이었어서 그 때 Supabase Storage를 사용할거라 좀 더 유심히 들었다.이번 Dropbox 프로젝트에서는 지난 TodoList 프로젝트를 했을 때 개인적으로 Meterial UI Tailwind를 사용할 때 마음에 들지 않았던 부분들이 있어서 한번 써보고 싶기도 했던 Shadcn/ui를 통해 css를 구현해봤다.수강 내용Section 4. Dropbox 클론코딩이번 섹션에서 배운 내용들은 다음과 같다.Storage 설정 (Bucket 생성 및 Policy 설정)파일 업로드, 검색, 수정, 삭제 기능드래그 앤 드롭을 통한 파일 업로드 기능 추가주로 이제 스토리지에 어떻게 접근해 파일들을 제어할 것인지가 핵심 내용이었다.export async function uploadFile(formData: FormData) { const supabase = await createServerSupabaseClient(); const file = formData.get("file") as File; const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET!) .upload(file.name, file, { upsert: true }); handleError(error); return data; }예를들어 다음과 같은 코드는 파일을 업로드할 때 사용하는 코드이다. upsert를 통해 insert + update 기능으로 같은 이름이 있다면 자동으로 수정해주는 기능이 있다는 것도 처음 알게 된 사실이다.프로젝트에서 한가지 아쉬운 점이 있었다면 파일을 검색할 때 예를들어 A 를 검색했다면 A 로 시작하는 파일 이름만 검색된다는 점이었다. 따라서 이 부분을 개인적으로 한번 수정해보기도 했다.export async function searchFiles(keyword: string = "") { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET!) .list(); // 전체 파일 목록 가져오기 handleError(error); return data ? data.filter((file) => file.name.includes(keyword)) : []; }다음과 같이 keyword를 받아오면 supabase storage의 전체 파일 리스트들을 가져오고, 그 안에서 filter을 통해 keyword가 포함된 데이터들만 표시되도록 구현해봤다. 이렇게 하면 검색 키워드가 포함된 파일 이름들은 모두 나타나게 된다. 드래그 앤 드랍 기능도 구현했는데 이는 react-dropzone 라이브러리를 사용했다.const onDrop = useCallback((acceptedFiles: File[]) => { // Do something with the files }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });npm에 적혀있는 공식 사용법인데, 기존에 사용했던 form을 삭제하고 div로 바꾼 다음 div와 input에 getRootProps getInputProps를 넣고 각각에 적절한 로직들을 넣어주면 드래그 앤 드랍을 통한 파일 업로드 기능도 정상적으로 이루어진다.미션미션은 마지막으로 업데이트 된 날짜를 이미지 카드에 표시해주는 내용이었다. supabase에서 이미지 리스트들을 불러오면 각각의 이미지들은 다음과 같은 구조를 띈다.따라서 lastModified 나 updated_at을 작성해주면 된다. (둘이 무슨 차이가 있는지 잘 모르겠다.....) 날짜 포맷은 지난 Todolist때 사용했던 것 그대로 사용했다. :)결과 화면은 다음과 같다.마무리파일 업로드를 Supabase에 어떤 방식으로 해야할 지 알게 된 시간이었다. 그리고 지난번 기초를 잘 잡아두니 (초기 세팅 등) 별다른 불편함 없이 프로젝트를 새로 시작할 수 있어서 역시 초기 세팅이 중요하다 생각됬다. 또한 강의만 따라 듣는 것이 아닌 부족한 점을 개선하고 내 방식을 추가해서 작업했다는 점도 고무적이었다.다음 프로젝트는 Netflix 프로젝트인데 사실 넷플릭스 프로젝트는.. 다른 강의에서도 몇번 해봤던 내용이지만 특히 그 중 기대하는 점은 SEO에 관한 내용이 있다는 것이었다. Next.js를 사용하는 큰 이유 중 하나가 바로 SEO 관련인데 이 부분에 대해 어떤 내용일지가 궁금하다. 예를 들어 Next.js는 자체적으로 sitemap이나 robots.txt 기능을 제공한다는 점과 강의에서도 말씀해주셧든 page.tsx가 서버 컴포넌트여야 하는 이유 등 어떻게하면 Next.js만이 가지는 장점을 최대한 효율적으로 활용하면서 프로젝트를 만들 수 있을지 기대가 된다. 

웹 개발웹개발Next.js프론트엔드Supabase

희주

[인프런 워밍업 클럽 3기 풀스택] 2주차 발자국

학습 내용이번 주에는 Supabase Storage를 활용하여 파일 업로드가 가능한 Minibox 프로젝트 클론코딩을 진행했다.Supabase Storage Bucket 생성 및 접근 정책 설정 방법파일 업로드, 검색, 삭제 Server Action과 기능 구현같은 파일명이 있을 경우 덮어쓰는 upsert 옵션storage bucket에 올라간 이미지 URL 구조를 확인해보고, 업로드 응답으로 받는 path를 활용해 직접 URL을 만드는 getImageUrl() 함수 작성해 화면에 이미지 표시react-dropzone 라이브러리를 적용해 Drag & Drop으로 파일 업로드 기능 구현react-dropzone 라이브러리의 useDropzone에 multiple 옵션을 적용하고 멀티 파일 업로드 구현1주차에 만든 TODO List 프로젝트 코드를 활용해 빠르게 초기 설정을 마치고, 저번 주 학습 내용에 익숙해지면서 Supabase Storage 사용법과 파일을 다루는 방법을 익힐 수 있었다. 미션Dropbox Clone 프로젝트에 파일의 마지막 수정(업로드) 시간을 표시하는 기능을 추가하세요.파일 목록에서 각 파일의 “마지막 수정 시간”을 표시 📌 참고 문서: Supabase Storage - 파일 목록 가져오기 https://supabase.com/docs/reference/javascript/storage-from-list위의 참고 문서에서 파일 리스트를 가져올 때 받는 data에 updated_at 값이 포함된다는 것을 알 수 있었고, 이를 활용해 파일의 마지막 수정(업로드) 시간을 표시하기로 했다.파일들의 data를 받아오면 DropboxImage 컴포넌트에 전달된다. 이 컴포넌트에서 각 파일 data(image)의 image.updated_at(마지막 수정 시간)을 파일 이름 바로 아래에 표시해주었다. 이때 TODO List 미션 때 작성했던 날짜 포맷 함수를 적용했다.... {/* FileName */} <div className="">{image.name}</div> {/* Updated_at */} <div>{formatDate(image.updated_at)}</div> ... 마무리이번 주는 파일 업로드와 Drag & Drop 등 자주 쓰이는 유용한 기능을 따라 구현해볼 수 있었어서 앞으로 필요할 때 쉽게 적용할 수 있을 것 같다. 다만 한글 파일명은 업로드되지 않는 문제와 첫 글자부터 입력해야만 뜨는 검색 기능도 별도로 개선이 필요할 것 같다.🥺또한 이번주에는 중간점검 라이브가 진행됐는데, 질문에 대해 자세히 답해주셔서 앞으로의 방향 설정에도 많은 도움이 되었고 짧지만 유익한 시간이었다! 벌써 진도의 절반이 지나가고 있는데 지금까지의 내용도 잘 보강하면서 나머지 강의와 미션도 끝까지 해내고 싶다🙂

풀스택풀스택웹개발Next.jsSupabase

희주

[인프런 워밍업 클럽 3기 풀스택] 1주차 발자국

학습 내용1주차에는 주로 사용할 기술들에 대한 소개와 기본 문법을 배우고 이를 이용해 TODO List를 만들었다.Firebase 이후 등장한 Supabase에 대해서 배웠다.오픈소스 프로젝트여서 자체 서버구축이 가능하고 특히 개인/소규모 풀스택 개발에 필요한 것이 대부분 갖춰져 있다는 장점이 있어 앞으로 진행할 프로젝트에 적절한 서비스임을 느꼈다. 부족한 문서 한글화가 단점이라고 하셨는데 강의에서 설정 방법을 자세히 알려주셔서 초보인 나도 쉽게 적용시킬 수 있었다.Next.js는 풀스택 개발에 최적화된 프레임워크로, SSR을 지원하고 별도 서버 구축 없이도 API 구축이 가능하다.이외에도 TailwindCSS(+MaterialUI), Recoil, React Query(TanStack Query)에 대해 배우고 사용해보았다.기존에 GraphQL을 써봤는데, 이번 기회에 궁금했던 React Query의 장점과 기본 문법을 알게 되어서 좋았다. 사용법 자체는 비슷하기도 해서 금방 적응할 수 있을 것 같았다.나는 프론트엔드만 더듬더듬 배운 적이 있고 제공되는 백엔드 API를 사용하기만 했었는데(만드는 걸 아주 간단하게 배운 적 있는데 엄청 어려웠다) Server Action 함수 만들고 호출하기만 해도 백엔드 없이 직접 DB를 조작할 수 있다는 게 신기했다.  미션생성된 TODO의 생성 시간을 저장하고 이를 표시하는 기능을 추가하세요.TODO 항목 옆에 생성 시간을 표시하기날짜 포맷 함수 만들기// utils/formatDate.ts export const formatDate = (dateString?: string): string => { if (!dateString) return ""; const date = new Date(dateString); const yyyy = date.getFullYear(); const mm = String(date.getMonth() + 1).padStart(2, "0"); const dd = String(date.getDate()).padStart(2, "0"); const hh = String(date.getHours()).padStart(2, "0"); const min = String(date.getMinutes()).padStart(2, "0"); const ss = String(date.getSeconds()).padStart(2, "0"); return `${yyyy}-${mm}-${dd} ${hh}:${min}:${ss}`; };생성 시각은 이미 supabase에 있으므로 created_at 필드 값을 가져와 사용나중에 완료 시각이 추가되어도 생성 시간은 위치가 바뀌지 않게 위쪽에 배치<> <p className={`flex-1 ${completed && "line-through"}`}>{title}</p> <div className="grid grid-rows-2 items-end pr-1"> <p className="text-xs text-right text-gray-500"> <i className="fas fa-pen pr-1" /> {formatDate(todo.created_at)} </p> <p className="text-xs text-right text-gray-500"></p> </div> </>completed_at 필드를 추가하여 완료한 시간도 함께 저장하기created_at과 같이 supabase에 completed_at 필드부터 추가(Allow Nullable 체크)이후 npm run generate-types 실행해 타입 파일도 수정todo가 체크되어 completed가 true일 때는, completed_at에 완료 시각을 저장하도록 updateTodo Server Action을 수정export async function updateTodo(todo: TodoRowUpdate) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase .from("todo") .update({ ...todo, updated_at: new Date().toISOString(), completed_at: todo.completed ? new Date().toISOString() : null, }) .eq("id", todo.id); if (error) { handleError(error); } return data; }마지막으로 completed_at도 포맷팅하여 완료 시각을 표시하고, 조건부 렌더링을 적용해 완료 시각이 없을 때는 아이콘과 시각 모두 표시하지 않도록 작성<> <p className={`flex-1 ${completed && "line-through"}`}>{title}</p> <div className="grid grid-rows-2 items-end pr-1"> <p className="text-xs text-right text-gray-500"> <i className="fas fa-pen pr-1" /> {formatDate(todo.created_at)} </p> <p className="text-xs text-right text-gray-500"> {todo.completed_at ? ( <> <i className="fas fa-check mr-2" /> {formatDate(todo.completed_at)} </> ) : null} </p> </div> </>마무리이번 주 학습에서는 스스로 부족한 점을 많이 느꼈던 것 같다... 처음 배운 것, 지금까지 잘 몰랐던 것도 많았고 버전 문제 등 사소한 이슈를 많이 겪어서 시간이 꽤 들었다.그래도 생각보다 간단하게 Todo List가 만들어지는 걸 보면서, 완강할 때쯤 되면 나만의 프로젝트도 만들 수 있겠다는 자신감이 생기고 있다🙂 아직 너무나 익숙하지 않지만 클론하다 보면 조금씩 감이 잡힐 것 같다.미션을 진행하면서 발전시킬 수 있는 부분도 많을 것 같은데 이번 주에는 시간을 거의 내지 못해 아쉬웠다. 다음 주에는 더 열심히 따라가고 싶다!

풀스택풀스택웹개발Next.jsSupabase

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 1주차 발자국 - TODO List 구현

이제 막 3개월차에 들어가는 새내기 개발자. 일을 다니면서 퇴근 후 개인공부 또는 개발을 하며 멋있는 삶을 보내는 나날을 상상했지만 현실은 퇴근하면 누워있기 바쁜 그런 회사원A. 그러던 차에 인프런에서 스터디를 한다는 소식을 들었다.이런 프로젝트가 있다는 것은 이미 알고 있었지만, 이전에는 이미 들은 강의이거나 기술이 맞지 않아 참여를 하지 않았었는데 마침 관심있던 Supabase와 요즘 실무에서도 사용중인 Next.js를 결합한 프로젝트 강의라니! 심지어 할인된 가격에! 이건 안할 수 없겠다 싶어서 바로 신청했다. 솔직히 사놓고 안듣고있는 강의가 넘치는데 이런 프로그램에라도 참여해야 강의를 완강할 수 있겠다 싶은 생각도 있긴했다. ㅠㅠ 수강 내용Section 1, 2 : OT 및 기술 소개Section 1과 2에선 전체 프로젝트에 대한 소개와 사용 기술에 대한 설명이 주를 이뤘다.이번 강의로 다음과 같은 기술 스택들을 배울 수 있다고 했다.Supabase에서는 Storage, Database, Auth, Realtime 등 Next.js 14, Typescript, Tailwind CSS, Meterial UI Tailwind, Recoil, Tanstack QueryAWS, Vercel, GoDaddy(Domain) 확실히 기존에 개인 프로젝트를 할 때 Firebase를 사용했었는데 Firebase같은 경우는 나온지 오래되서 그런지 레퍼런스들이 굉장히 많았다면 Supabase는 그런 점이 좀 부족하다 생각했었는데 강의로 그런 부분들을 채울 수 있어서 좋았다. 실제로 혼자 한번 Supabase를 설정하려고 했었는데 안되서 한참 찾아봤던 문제를 강의에서 짚어주신 부분도 있었다.강의에서도 좀 중요하게 강조했던 부분이 있다면 Tanstack Query가 아닐 까 싶다. 사실 좋다 좋다 해서 사용하지 왜 좋은지 왜 사용하는지 잘 알지 못하고 썼었는데 이번 기회에 왜 사용하는지, 어떻게 사용하는지 좀 더 잘 알게 된 것 같아서 나중에 효율적으로 사용할 수 있을 것 같다는 생각이 들었다.Tanstack Query (React Query)서버와 통신할 때 관리해야 하는 상태들이 많이 다양한데, 이런 상태 관리를 클라이언트에서 사용하는 데 도움을 주는 라이브러리.fetching, caching, 서버 데이터와의 동기화를 지원해주는 라이브러리캐싱 : 캐싱을 통해 동일한 데이터에 대한 반복적인 비동기 데이터 호출을 방지하고, 불필요한 API 콜을 줄여 서버에 대한 부하를 줄이는 결과를 갖는다.Client 데이터와 Server 데이터 간의 분리 : 프로젝트 규모가 커지고 관리해야 할 데이터가 넘치다보면 클라이언트에서 관리하는 데이터와 서버에서 관리하는 데이터가 분리될 필요성을 느낀다.서버에서 가져오는 데이터 : react-query 클라이언트에서 관리하는 데이터 : recoil, zustand ...useQuery : 데이터를 가져오는 작업에 사용useMutation : 데이터를 변경하는 작업(PUT, POST, DELETE)에 사용queryClient.invalidateQueries : queryClient를 사용해 쿼리 요청을 다시 진행할 수 있다. (다른 곳에서 refetch를 진행하기 위함Section 3. TODO LIST 만들기(UI 구현은 블로그 글로는 생략...)본격적으로 프로젝트를 만들어보는 시간이다. Supabase에는 Table Editor, SQL Editor, Database, Authentication, Storage 등 어떤 기능들이 있는지 각각 자세하게 설명해주셨다.그리고 Supabase를 사용하기 위한 환경변수를 설정하고, Supabase Database를 사용할 때 쉽게 types를 추가하기 위해 script를 추가했는데 처음엔 몰랐는데 나중에 미션을 진행하다보니 자동으로 supabase에 있는 table에 대한 타입을 추가해준다는게 굉장히 편리했다."scripts": { // dev, build, start, lint "generate-types": "npx supabase gen types typescript --project-id [project_id] --schema public > types_db.ts" }그리고 이제 Supabase 사용하기 위한 client server middleware 설정도 진행했다. 혼자 공식문서를 봤으면 한참 해멨을 텐데 간단하게 설정이 완료됬다. 이 부분은 나중에 supabase로 개인 프로젝트를 진행할 때 요긴하게 사용할 듯.이제 Next.js Server Action을 구현했다.server action : api 구현 없이 함수 호출만으로도 api를 대신할 수 있는 기능getTodo, createTodo, updateTodo, deleteTodo를 만들었다. 그 중 getTodo와 createTodo를 보면 다음과 같다.export async function getTodos({ searchInput = "" }): Promise<TodoRow[]> { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase .from("todo") .select("*") .like("title", `%${searchInput}%`) .order("created_at", { ascending: true }); if (error) { handleError(error); } return data; }from : 가져올 Table 선택 select : 가져올 부분 선택like : title에 searchInput이 앞뒤로 들어가 있는 부분이 있다면 모두 가져온다.order : 정렬export async function createTodo(todo: TodoRowInsert) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.from("todo").insert({ ...todo, created_at: new Date().toISOString(), }); if (error) { handleError(error); } return data; }created_at : 사용자가 잘못 입력할 수도 있어 데이터가 생성될 때 서버에서 자동으로 날짜가 생성되도록 구현actions들도 모두 구현했으니 이제 DOM에 이벤트를 달아줄 일만 남았다. 위에서 Tanstack Query를 사용하는 방법을 배웠듯 서버에서 데이터를 가져오는 것은 useQuery를 사용하고 데이터를 생성, 수정, 삭제하는 부분은 useMutation을 사용해 구현할 것이다.const { data, isLoading, refetch } = useQuery({ queryKey: ["todos"], queryFn: () => getTodos({ searchInput }), });const { mutate, isPending } = useMutation({ mutationKey: ["todos"], mutationFn: () => createTodo({ title: "New Todo", completed: false, }), onSuccess: () => refetch(), });useQuery useMutation을 담당하는 부분들의 일부를 한번 가져왔다. 이렇게 데이터를 get 할 때는 useQuery 그리고 create 등을 할 때 useMutation을 사용했다.강의를 모두 마치면 CRUD 기능을 모두 할 수 있는 TODO List가 완성된다.미션1주차 미션은 "생성 시간과 완료 시간"을 표시하는 것.생성 시간은 이미 존재하므로 완료 시간만 Supabase Todo Table에 completed_at 컬럼을 추가했다. 이 때 아까 위에서 말했듯 generate script를 통해 쉽게 table type을 정의할 수 있어서 간편했다.actions에 updateTodo 부분에 completed_at을 추가하고 todo.completed가 true일 경우 새 날짜를 작성하도록 구현했다.const { data, error } = await supabase .from("todo") .update({ ...todo, updated_at: new Date().toISOString(), completed_at: todo.completed ? new Date().toISOString() : null, }) .eq("id", todo.id ?? 0);그리고 시간을 표시하기위해 Date를 바꾸는 함수를 추가했다. (YYYY-MM-DD HH-MM-SS 형식)export const formatDate = (dateString: string) => { if (!dateString) return ""; const date = new Date(dateString); const formattedDate = date.toLocaleDateString("en-CA"); const formattedTime = date.toLocaleTimeString("en-GB", { hour12: false, }); return `${formattedDate} ${formattedTime}`; // 이부분 ${} 이 연속된 부분인데 현재 코드 블럭에서 이상하게 나온다. };따라서 생성 날짜와 수정된 날짜를 표시하면 다음과 같이 결과물이 완성된다.마무리 직장과 병행하며 프로그램을 수행한다는 것이 조금 어려웠지만 무사히 1주차 미션들을 달성해서 뿌듯하다. 혼자라면 중간에 포기하거나 조금 루즈해지거나 했을텐데 다들 열심히 사는 것 같아 나도 뒤쳐지지 않도록 노력했다.개인적으로 이번 강의를 모두 마치고 목표가 있다면 Supabase + Next.js를 통해 나만의 블로그를 만드는 것. 강의를 들으면서 나중에 이 기능은 어떻게 활용하면 좋겠다라는 상상도 해보고 하는 시간들이 즐거웠다. 또 강사님께서 차근차근 친절히 알려주셔서 더 쉽게 학습됬었던 듯 하다. 개인적으로 supabase이외에도 react query를 좀 더 상세하게 알 수 있었던 시간이라 (위에서도 말했듯 이전에 사용할 때에는 그냥 좋다니까 썼지 제대로 쓴다는 느낌은 받지 못했다) 굳이 fullstack 스택이 아니더라도 front-end 개발자 입장에서도 한단계 스킬 업한 느낌이었다.앞으로 3, 4주차에 좀 타이트하게 진행된다고 했는데 이번주는 스케쥴에 딱 맞게 진행해서.. 다음주에는 조금 더 신경써서 시간을 할애해봐야겠다. 스터디원들 모두 화이팅...! ❣

웹 개발웹개발풀스택SupabaseReactNext

[한경닷컴](수료 후 인턴십 100% 연계) 실시간 데이터 자바 웹서비스 개발 교육과정

  [교육 소개]- 수료 이후 100% 인턴 연계 실시 - 웹 기초 이해 및 자바, 파이썬, 리엑트 프로그래밍 언어 학습- 백엔드, 프론트엔드 기술 학습/ 클라우드(AWS)구축 기술 학습- 실무 프로젝트 2회 실시 및 포트폴리오 제작 [교육혜택]- 실제 데이터 기업 탐방을 통한 실무현장 경험 제공- 수료 후 IT 기업과의 인턴십 매칭 및 참여 기회 제공 - 수료 후 추가 프로젝트 필요 시 기업연계 프로젝트 기회 제공   - 전액 국비 무료 교육(1인 1,600만원)- 매월 최대 316,000원 훈련수당 지급- 수료자 기업연계 및 면접 기회 제공- IT 전문 취업컨설턴트와의 1:1 상담 제공- 무제한 취업지원 및 채용연계 정보 제공 [인턴십 기업 예시 리스트] [일정]- 2024년 12월 31일(화) ~ 2025년 6월 27일(금)[시간]- 월~금 9시~18시(8시간)[교육대상]- 재학생(3~4학년), 졸업자, 미취업자- 내일배움카드 발급 가능자 및 예정자- 6개월 몰입교육 가능한 분[접수기간]2024.12.04(수) 오후 6시까지 접수 마감(선착순 30명 배정)접수신청방법 (1~2번 中 편한 형태로 진행)1) 아래 링크 참가신청 작성- https://forms.gle/oqZPXf26Fsq8qmxf9 (해당 링크로 신청 가능)2) 성함 및 개인 연락처 기재 후, 해당 메일로 우선 신청 가능 (담당자 별도 연락 예정) [커리큘럼]링크: 교과편성 연계표 | HRD-Net1) 웹 기초 이해 및 자바, 파이썬, 리액트 프로그래밍 언어 학습2) 백엔드, 프론트엔드 기술 학습 / 클라우드(AWS) 구축 기술 학습3) 실무 프로젝트 2회 실시 및 포트폴리오 제작 / 프로젝트 성과발표회 및 수료증 제공4) 금융, 유통, 제조, 물류 등 다양한 분야에서 활용되는 대용량 데이터 처리 학습4) 수료 전후, 전문 취업컨설팅 및 기업연계 실시 (조기 기업연계 가능 및 수료 후 6개월 간 사후관리 진행) [문의처]Tel. 02-6956-5983메일 문의 [info@k-jobc.co.kr] 

개발자데이터자바웹서비스웹개발국비교육

vin

[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 4주차 발자국

마지막 주차인 4주차는 지금까지 개발한 어디민 페이지의 뷰를 개발하였고, 스프링 시큐리티를 이용한 로그인 개발, 로그 저장 등의 기능을 구현 하였다. 프로젝트를 마무리 짓고 최종적으로 docker와 구글 클라우드를 이용해서 배포를 하였다. 어드민 화면 개발 1부트스트랩 템플릿 적용 시 코드 분석 및 TODO 체크 기능 해제: 부트스트랩 템플릿을 사용해 UI를 구성할 때 코드 변경 사항이 많아 analyze code와 check TODO 기능이 오래 걸려, 이를 해제하여 커밋 및 푸시 시간을 줄임.사이드바 개발에서 th:classappend, th:attr data-bs-target, th:classappend show 사용: Thymeleaf의 th:classappend를 이용해 동적으로 클래스를 추가하고, th:attr을 통해 부트스트랩의 data-bs-target 속성을 설정하여 UI 요소를 제어함. show 클래스를 사용해 사이드바를 열고 닫는 동작을 구현.뷰 레이아웃 개발 및 테이블 페이지(Table) 구현: HTML 템플릿에서 레이아웃을 설계하고, 데이터가 표시될 테이블 형태의 페이지를 구성. 이를 통해 표 형태로 데이터를 보기 쉽게 표시./*<![CDATA[*/ 구문 사용과 [[${pageName}]] 바인딩: Thymeleaf에서 스크립트를 안전하게 사용하기 위해 CDATA 구문을 사용하고, ${pageName}을 통해 페이지 이름을 동적으로 바인딩하여 화면에 표시.테이블 아이디 tableName 설정: HTML에서 테이블을 식별하기 위한 아이디를 tableName으로 설정하여 JavaScript 등에서 쉽게 접근 가능하도록 함. 어드민 화면 개발 2테이블 페이지(Form) 개발: 사용자가 데이터를 입력하고 제출할 수 있는 Form 페이지를 구성. 입력된 데이터를 서버로 전송하고 처리하는 역할을 함.onsubmit : Form이 제출되기 전에 특정 JavaScript 함수를 실행하여 데이터를 검증하거나 추가 작업을 수행할 수 있게 해주는 이벤트.대시보드와 로그인 기능 개발스프링 시큐리티를 이용한 로그인 개발: 스프링 프레임워크에서 제공하는 시큐리티 모듈을 사용해 사용자 인증 및 권한 관리를 구현. 안전한 로그인 기능을 제공.패스워드 인코더 및 필터 체인 개발: 패스워드 보안을 위해 PasswordEncoder를 사용해 비밀번호를 암호화하고, 인증 과정을 제어하기 위해 필터 체인을 설정함.AntPathRequestMatcher 사용: 특정 URL 경로와 HTTP 메서드에 대해 시큐리티 필터링을 설정하는 데 사용. 로그아웃 등의 특정 경로에 대해 시큐리티를 설정할 때 유용함.구글 클라우드 플랫폼으로 프로젝트 배포Docker로 MySQL 실행 및 컨테이너 설정: Docker 이미지를 사용해 MySQL 데이터베이스를 컨테이너로 실행하고, 필요한 포트와 환경 변수를 설정하여 데이터베이스를 관리.ports, command, volumes 설정:ports: 호스트와 컨테이너 간의 포트를 매핑하여 외부에서 데이터베이스에 접근할 수 있도록 함.command: 컨테이너가 시작될 때 실행할 명령어를 정의. 예를 들어, MySQL의 문자 집합을 설정하는 명령어를 추가.volumes: 컨테이너의 데이터가 호스트에 영구적으로 저장되도록 설정하여 컨테이너 재시작 시 데이터가 손실되지 않도록 함.프로젝트 컨테이너 설정 및 depends_on 사용: Spring 애플리케이션을 실행하는 컨테이너와 데이터베이스 컨테이너를 설정하고, depends_on을 통해 MySQL이 먼저 실행된 후 애플리케이션이 실행되도록 순서를 지정.호스트 포트 (Host Port): 호스트 컴퓨터에서 사용되는 포트입니다. 예를 들어, 3306:3306에서 앞의 3306이 호스트 포트입니다. 이는 Docker를 실행하는 컴퓨터에서 접근 가능한 포트 번호를 의미합니다. 다른 컴퓨터나 서버에서 이 호스트의 IP 주소와 포트를 사용하여 Docker 컨테이너의 서비스에 접근할 수 있습니다.컨테이너 포트 (Container Port): 컨테이너 내부에서 서비스가 실행되는 포트입니다. 3306:3306에서 뒤의 3306이 컨테이너 포트입니다. 이는 컨테이너 내부에서 MySQL 서비스가 동작하는 포트를 의미합니다. 컨테이너 내의 애플리케이션은 이 포트를 통해 데이터베이스와 통신합니다.Docker로 프로젝트 빌드Google Cloud Platform에서 Compute Engine 인스턴스 생성: 애플리케이션을 배포할 가상 머신을 생성하여, Docker 컨테이너를 실행할 환경을 제공.도커 허브로 푸시 시 커맨드로 진행: Docker Desktop에서 푸시 시 발생하는 오류를 해결하기 위해 명령어를 사용하여 직접 푸시.Compute Engine에서 도커 컨테이너 실행: Google Cloud에서 생성한 인스턴스에 도커 컨테이너를 실행하여 애플리케이션을 운영.메모리 스왑 설정 및 Nginx 사용: 하드디스크의 일부를 메모리처럼 사용하는 메모리 스왑을 설정하고, Nginx를 사용해 애플리케이션의 로드 밸런싱 및 리버스 프록시 역할을 설정.도메인 연결 및 HTTPS 적용: 애플리케이션에 도메인을 연결하고 SSL 인증서를 적용하여 HTTPS를 통해 안전하게 서비스.[미션 6] MySQL에 내 데이터 넣기문제 상황: Docker로 실행한 MySQL과 DBeaver에서 연결된 데이터베이스의 내용이 달랐음.원인: 과거 MariaDB 사용 이력이 있어, MariaDB와 MySQL이 서로 다른 포트로 연결되어 데이터가 불일치.DBeaver에서는 MariaDB에 연결되고, Docker에서는 MySQL에 연결되는 상황 발생.해결 방법: MariaDB의 서비스를 중단하여 MySQL만 실행되도록 설정하여 문제를 해결.[미션 7] 내 포트폴리오 도메인 공유하기문제 상황: Google Cloud Compute Engine에서 Docker Hub의 이미지를 pull하려 했으나, Docker push가 되지 않아 pull이 실패.원인: Docker Desktop 4.3.3 버전 이후로, Docker Desktop을 통한 push에서 오류 발생.해결 방법: Docker Desktop 대신 명령어를 사용해 push하여 문제를 해결.

웹 개발워밍업클럽백엔드프로젝트웹개발백엔드

vin 2024.10.27
vin

[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 3주차 발자국

이번 주차에서는 포트폴리오 사이트 화면과 어드민 기능을 개발하며, 사이트의 삽입, 수정, 조회 API 구현과 인터셉터 설정을 완료하였다. 타임리프와 부트스트랩을 활용한 뷰 템플릿 구성, 데이터 처리, 예외 관리 등 다양한 기능을 학습하며 웹 개발의 전체적인 흐름을 이해할 수 있었다. 포트폴리오 사이트 화면 개발하기 1RestController와 Controller의 차이점RestController: JSON 데이터를 반환하며, 주로 RESTful API를 구현할 때 사용. HTML 뷰 템플릿을 반환하지 않음.Controller: HTML 등 뷰 템플릿을 반환하기 위해 사용.부트스트랩과 타임리프 프래그먼트 사용부트스트랩 템플릿 적용: 부트스트랩 사이트에서 템플릿을 다운로드하여 프로젝트에 적용.타임리프 프래그먼트 사용: head, footer, navigation 등의 공통 영역을 분리하여 프래그먼트로 구성.th:fragment: 프래그먼트의 이름을 지정해 원하는 곳에서 재사용.th:replace: 프래그먼트를 뷰 파일에 포함할 때 사용.타임리프 문법 활용th:each: 컨트롤러에서 전달된 모델 데이터를 반복하여 출력.th:text: 서버 데이터를 HTML 요소에 텍스트로 삽입.target="_blank": 링크 클릭 시 새 탭에서 열리도록 설정.포트폴리오 사이트 화면 개발하기 2동적 콘텐츠와 레이아웃 처리th:fragment에 파라미터 전달: 프래그먼트에 파라미터를 전달해 동적 콘텐츠를 처리.th:block 사용 이유: 불필요한 태그 없이 블록을 형성해 th:each와 조건부 렌더링(th:if)을 함께 사용하기 위함.d-inline 태그: 인라인 요소로 배치하여 레이아웃 조정을 쉽게 함.레이아웃 작업: 공통 레이아웃을 th:fragment로 정의해, 수정 시 모든 페이지에 자동 반영되도록 구성.인터셉터의 역할과 설정인터셉터: 컨트롤러보다 앞단에서 동작하며, 여러 컨트롤러의 요청을 잡아 공통적인 처리를 수행.addInterceptors 오버라이딩: WebMvcConfigurer에서 인터셉터를 등록할 때 사용.모든 경로를 대상으로 하며, 정적 자원 경로(CSS, JS 등)는 제외.presentationInterceptor의 HandlerInterceptor 추가:preHandle: 컨트롤러에 도달하기 전 실행.postHandle: 컨트롤러의 응답 후 실행되나, 예외가 발생하면 동작하지 않음.afterCompletion: 예외 발생 여부와 상관없이 항상 실행.HttpInterface 사용: 클라이언트 정보를 수집해 기록.어드민 공통 기능 개발하기클래스 생성 및 관리admin 패키지: 컨트롤러, 데이터 처리, 예외 처리, 인터셉터, 보안 관련 클래스를 구성.예외 처리와 로깅Throwable: 모든 오류의 최상위 클래스.Error: 복구할 수 없는 오류.Exception: 애플리케이션이 복구 가능한 오류.Checked Exception: 컴파일 시점에 예외가 검출됨.Unchecked Exception: 런타임 시점에 발생.ControllerAdvice: 인터셉터와 유사하게 동작하며, 예외 처리를 담당.@ExceptionHandler: 특정 예외를 처리하는 메소드.Logger 사용: LoggerFactory.getLogger를 활용해 로그를 기록.DTO 개발companion object: 클래스 내부에서 정적 메소드와 변수를 정의할 때 사용.vararg: 가변 인자를 받아 여러 파라미터를 전달할 수 있도록 함.filterings: 데이터에서 제외할 필드를 관리.classInfo: 클래스의 메타데이터와 정보를 담음.인터셉터 추가postHandle 오버라이딩: 메뉴 데이터를 modelAndView에 추가해 특정 페이지에 전달.addInterceptors 설정: admin 경로에 인터셉터를 추가하며, 정적 자원(CSS, JS 등)은 제외.데이터 조회 기능 개발하기조회 기능 구현context 패키지: 화면과 관련된 설정을 서버에서 관리.orElseThrow: 데이터가 없을 경우 예외를 발생.@field: 필드 유효성 검사를 수행.@PathVariable: 경로 변수를 통해 데이터를 전달.@RequestBody: 요청 본문 데이터를 객체로 변환.@Validated: 입력 데이터의 유효성을 검사.데이터 삽입, 수정, 삭제 기능 개발하기DTO와 데이터 처리form 패키지: DTO 역할을 하는 클래스들을 생성해 데이터 전달에 활용.ifPresent 사용ifPresent: 값이 존재할 때만 특정 동작을 수행하도록 처리.[미션4] 조회 REST API 만들기이번 미션에서는 제품, 브랜드, 카테고리, 재고에 대한 조회 API를 개발하고, 각 경로(/api/products, /api/brands, /api/categories, /api/stocks)에 대해 테스트 코드를 작성하였다.이번 미션에서는 조회 API 개발 및 테스트 코드 작성을 통해 RESTful API의 기본 구조를 익히고, Null 처리와 영속성 관리를 경험하였다. 특히 **?.let {}**과 ?: 엘비스 연산자를 활용해 null-safe한 코드를 작성하는 방법을 익혔으며, 서비스 계층 분리를 통해 더 효율적인 설계를 계획하게 되었다.ProductDTO에서 발생한 Null 처리 이슈와 해결조회 API를 구현하는 과정에서 ProductDTO 내의 brand와 category가 null일 경우 NullPointerException이 발생하는 문제가 발견되었다. 이를 해결하기 위해 ?.let {} 구문을 사용해 null-safe하게 처리하였다.?.let {}의 역할?.let {}: 객체가 null이 아닐 때만 코드 블록 내부를 실행한다.예시:brand = product.brand?.let { BrandDTO(it) }product.brand가 null이 아닐 경우에만 BrandDTO를 생성하고, 그렇지 않으면 실행되지 않음.엘비스 연산자(?:) 사용조회 로직에서는 **엘비스 연산자 ?:**를 사용하여, 값이 null일 경우 대체 동작을 수행하도록 하였다.?: 연산자: 왼쪽의 값이 null이면 오른쪽 값을 반환한다. product = stock.product ?: throw IllegalArgumentException("제품정보를 찾을 수 없습니다..")만약 stock.product가 null이면 IllegalArgumentException을 던지도록 처리.영속성 설정: Cascade = PERSIST재고와 관련된 엔티티를 영속성 컨텍스트에 포함시키기 위해, cascade = PERSIST 옵션을 사용하였다. 이 설정은 엔티티가 함께 영속화될 수 있도록 보장하며, 이를 통해 테스트 코드가 문제없이 성공적으로 실행되었다.재고 수량 설정과 서비스 계층 분리 계획현재 재고 테이블의 재고 수량(stockCount) 설정은 Stock 엔티티의 메서드를 통해 처리하고 있다.하지만, 이 로직을 추후 삽입 및 수정 API에서 더 깔끔하게 관리하기 위해 서비스 계층으로 분리할 계획이다.

웹 개발백엔드워밍업클럽백엔드프로젝트웹개발

vin 2024.10.20
vin

[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 2주차 발자국

1주차에는 간단한 엔티티와 프로젝트 구조 작성을 완료하고, 이번 주에는 리포지토리, DTO, 서비스 계층을 전반적으로 개발했으며, 그 과정에서 테스트 코드도 함께 작성했다. 이를 통해 프로젝트의 핵심 기능들을 구조화하고, 안정성을 높이는 데 중점을 두었다. 데이터를 다루는 리포지토리 개발하기리포지토리 개발을 하였으며 역시 1주차와 달리 혼동스러운 어노테이션 및 개념과 처음 접하는 어노테이션 및 개념들이 나왔었다. 하나하나 상세히 어떤 역할을 하는지 강의에서 알려주셔서 따라가는데 문제 없었으나, 중간에 오류가 한번 발생하여 찾아보니 JPA영속성 관련 부분이였다.Projectskill에 데이터가 들어가지 않아 확인을 해보니 project 엔티티에서 skill 부분에 cascade 부분을 빼먹어서 함께 저장이 되지 않았다.1. Spring Component와 Bean 등록Component Bean 등록:스프링에서는 @Component, @Controller, @Service, @Repository 어노테이션을 통해 클래스를 Bean으로 등록@Component: 기본적인 스프링 컴포넌트 등록 어노테이션.@Controller: 웹 컨트롤러 계층에 사용, 요청을 처리하는 클래스.@Service: 서비스 계층에 사용, 비즈니스 로직을 처리하는 클래스.@Repository: 데이터 접근 계층에 사용, 데이터베이스와 상호작용하는 클래스.이들은 모두 @Component를 상속한 어노테이션으로, 역할에 따라 더 구체적으로 분류@ComponentScan:스프링이 애플리케이션을 시작할 때, 이 어노테이션을 사용하여 해당 패키지나 하위 패키지에서 컴포넌트를 스캔하고, DI(의존성 주입)를 위해 Bean으로 등록2. 스프링 프로파일과 @Profile 어노테이션@Profile:스프링에서 다양한 환경 설정을 관리할 때, 특정 프로파일이 활성화된 경우에만 빈(Bean)을 활성화할 수 있도록 하는 어노테이션예를 들어, @Profile("dev")는 "dev" 프로파일이 활성화될 때만 해당 빈이 등록3. 생성자 주입과 Bean 관리생성자 주입 방식으로 의존성을 주입받아 사용하는 것이 권장. 생성자 내부에서 미리 정의된 Bean을 주입받아 사용하고, 이 과정에서 스프링이 의존성 관리를 담당.@PostConstruct:Bean이 스프링 컨테이너에 등록된 후 실행되는 메서드에 붙일 수 있는 어노테이션. Bean의 초기화를 마친 후 로직을 처리할 때 사용.4. 로그 사용 권장System.out.println()은 성능상 좋지 않으며, 멀티 쓰레드 환경에서 비효율적이기 때문에 로그 라이브러리를 사용하여 로그를 남기는 것이 좋다.5. JPA와 영속성 관리 (Cascade)Cascade: JPA에서 엔티티의 상태 변화에 따라 연관된 엔티티도 함께 변경되도록 하는 옵션.CascadeType.PERSIST: 엔티티를 저장할 때 연관된 엔티티도 함께 저장.CascadeType.MERGE: 엔티티를 병합할 때 연관된 엔티티도 함께 병합.CascadeType.REMOVE: 엔티티를 삭제할 때 연관된 엔티티도 함께 삭제.CascadeType.REFRESH: 엔티티를 새로고침할 때 연관된 엔티티도 함께 새로고침.CascadeType.DETACH: 엔티티가 영속성 컨텍스트에서 분리될 때 연관된 엔티티도 분리.CascadeType.ALL: 모든 작업(PERSIST, MERGE, REMOVE, REFRESH, DETACH)에 대해 적용.예를 들어, Project 클래스에서 ProjectSkill 엔티티와의 연관관계에 CascadeType.PERSIST를 설정하지 않으면, Project가 저장될 때 연관된 ProjectSkill이 함께 저장되지 않을 수 있다. 6. JPA 성능 개선 (findById 오버라이딩)JpaRepository의 findById 메서드를 오버라이드하여 성능을 최적화할 수 있습니다. 예를 들어, 특정 조건에 맞는 쿼리를 커스터마이징하여 데이터베이스 성능을 개선할 수 있습니다.이러한 요소들은 스프링 애플리케이션 개발에서 빈 관리, 의존성 주입, JPA를 통한 데이터베이스 영속성 관리를 포함한 다양한 개발 과정에서 중요한 역할을 합니다. 리포지토리 테스트 하고 성능 개선하기개발한 리포지토리가 제대로 작성 되는지 테스트코드를 작성하였으며, 또한 리포지토리의 성능을 개선하기 위해 JPQL을 따로 작성하여 fetch 전략으로 LAZY를 사용하여도 깔끔하게 출력되게 개선하였다.리포지토리 테스트 코드 작성@TestInstance.Lifecycle.PER_CLASS: 메서드 간 독립적인 실행이 가능하며, 메서드 간 의존성이 제거됨.의존성 주입: 테스트 시 필요한 리포지토리나 서비스 등을 주입받아 테스트를 진행. 테스트 데이터 초기화: @BeforeAll을 사용하여 테스트에 필요한 데이터를 사전 생성. 프록시 객체와 Fetch 전략프록시 객체: JPA에서 연관된 엔티티를 가짜 객체로 생성하여 필요할 때만 데이터베이스에서 불러옴.LAZY 로딩: 필요할 때마다 쿼리를 날려 데이터를 가져옴. 하지만, 반복문을 돌 때마다 SELECT 문이 나가 성능 저하가 발생할 수 있음.EAGER 로딩: 한 번에 모든 연관된 엔티티를 조회하지만, 불필요한 데이터를 미리 가져올 경우 성능에 영향을 미칠 수 있음.3. Fetch Join과 N+1 문제 해결Fetch Join: JPQL에서 사용하여 연관된 엔티티를 한 번의 쿼리로 모두 조회하는 방법.장점: 추가적인 쿼리가 발생하지 않아 성능 최적화가 가능.단점: 필요 없는 데이터를 미리 가져올 경우 메모리 낭비 가능.N+1 문제: Lazy 로딩 시 발생하는 성능 문제로, 하나의 엔티티를 조회할 때 연관된 엔티티를 각각 추가 쿼리로 조회하여 비효율이 발생. Fetch Join으로 이 문제를 해결 가능.  4.Assertions의 assertThatassertThat: 테스트 코드에서 검증을 위한 메서드로, 다양한 조건에 맞는 검증을 수행할 수 있음.isEqualTo(): 두 값이 같은지 비교.isTrue() / isFalse(): 조건이 참인지, 거짓인지 확인.hasSize(): 리스트나 배열의 크기를 검증.contains(): 리스트가 특정 값을 포함하는지 확인.데이터를 조회하고 변환하는 서비스 개발하기도메인 패키지와 연결된 presentation 패키지를 생성하였고, 해당 패키지 안에 클래스, DTO, 리포지토리, 서비스 개발을 완료하였다. 또한, 서비스에 대한 테스트 코드도 작성했다.이번에는 도메인에서 개발했던 리포지토리 테스트와 달리 단위 테스트가 아닌 방법으로 개발하였다. 기존까지 나는 개발 환경에서 페이지를 띄운 후 하나하나 수동으로 테스트를 진행했지만, 이러한 방식은 운영 서버에서 테스트하기 어렵다는 단점이 있었다.따라서 테스트 코드를 작성하여 자동화된 방식으로 테스트하는 것이 실무적으로 더 안정적이라고 한다.1. @RestController로 REST API 구현@RestController: 스프링에서 RESTful 웹 서비스를 만들 때 사용하는 어노테이션으로, 컨트롤러에서 반환하는 데이터를 자동으로 JSON 형식으로 변환해 클라이언트에 전달한다. 주로 API 응답에서 사용되며, HTML이 아닌 데이터를 반환한다.2. @GetMapping과 @RequestMapping@GetMapping: HTTP GET 요청을 처리하는 어노테이션으로, @RequestMapping(method = RequestMethod.GET)의 간편한 대체 방식이다. 이를 통해 서버는 클라이언트의 GET 요청에 대해 데이터를 조회하고 응답할 수 있다.3. DTO (Data Transfer Object)DTO: 데이터 전송 객체로, 서버와 클라이언트 간 데이터를 주고받기 위한 객체다. 비즈니스 로직을 포함하지 않고 데이터의 전송에만 집중되어 있으며, 데이터를 안전하고 일관되게 전달하는 데 사용된다.4. Mockito와 @InjectMocks로 의존성 주입Mockito: 단위 테스트에서 실제 객체 대신 Mock 객체를 사용하여 독립적인 테스트를 가능하게 해주는 테스트 라이브러리이다. 이를 통해 외부 의존성을 최소화한 테스트를 할 수 있다.@ExtendWith(MockitoExtension::class): JUnit5와 Mockito를 통합해, 테스트 환경에서 Mock 객체의 자동 주입을 제공하는 어노테이션이다. 테스트 클래스에서 의존성 주입을 자동으로 처리하며, 의존성을 가진 객체들을 쉽게 Mocking할 수 있다.@InjectMocks: Mock 객체를 주입받아 의존성을 가진 객체를 독립적으로 테스트할 수 있도록 하는 어노테이션이다. Mock 객체를 통해 실제 객체 없이도 테스트할 수 있어, 테스트의 독립성과 효율성을 높여준다.미션 3REST API 설계하기이번 미션에서는 설계한 테이블을 기반으로 REST API를 설계하는 작업을 진행했다. 상품 API, 분류 API, 브랜드 API, 재고 API를 설계했으며, 각 API의 기능을 세부적으로 구현하였다.분류와 브랜드 API에서는 삭제 대신 useYn 필드를 변경하여 비활성화하는 API를 설계했다. 즉, 데이터를 완전히 삭제하지 않고 비활성화 상태로 변경하는 방식이다.재고 API에서는 재고 삭제 API를 설계하지 않았고, 재고를 수정할 때 기존 데이터를 수정하는 대신 새로운 데이터를 생성하여 수정 이력을 로그처럼 남길 수 있도록 설계했다.또한, HTTP 메서드 중 PUT과 PATCH 메서드의 차이점에 대해 고민했는데, 두 메서드의 차이는 다음과 같다:PUT: 전체 리소스를 대체하거나 새로 생성하는 데 사용된다.PATCH: 리소스의 일부만 수정하는 데 사용되며, 부분적인 변경에 적합하다.https://github.com/Malvin222/mission-backoffice 

웹 개발백엔드웹개발워밍업클럽스프링나만의포트폴리오사이트만들기

vin 2024.10.13
vin

[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 1주차 발자국

새로운 프로젝트 아이디어를 찾다가 인프런에서 위밍업클럽 2기를 시작한다는 소식을 듣고 신청하게 됐다. 강의 목록 중 특히 끌리는 강의가 있었고, 코틀린을 사용하는 점도 마음에 들었다. 그동안 코틀린을 접해본 적이 없어서 배우고 싶은 마음도 컸고, 새로운 기술들을 익히면서 동시에 다양한 사람들과 네트워킹을 할 수 있다는 부분이 마음에 들었다. 프로젝트 미리보기 + 웹 개발 기본 개념이번 강의에서는 웹 프레임워크, HTTP, 그리고 REST API와 같은 웹 개발의 기본 개념을 간략하게 학습했다.강의 시간이 짧아서 깊이 있는 설명은 없었지만, 웹 개발에서 필수적인 기초 개념을 알기 쉽게 설명해주었다. 덕분에 그동안 혼란스러웠던 부분들이 정리되었고, 전체적인 웹 개발의 기본 흐름을 이해할 수 있었다. 프로젝트 시작하기 + 데이터베이스 기본 개념데이터베이스와 JPA에 대해 이론적으로 학습하고, 실습으로 테이블 설계와 개발 환경을 구성하여 프로젝트를 시작했다.이전에는 주로 마이바티스(MyBatis)를 사용해 DB와 연결했지만, JPA에 대해서는 개념만 알고 있었다. 이번 강의를 통해 JPA의 기본 개념과 동작 방식을 명확히 이해하게 되었고, Spring Initializr 사이트를 통해 강의에서 사용할 개발 환경을 설정하고 프로젝트를 생성했다. 프로젝트 기초 설정하기코드 형상 관리를 위해 git과 GitHub를 사용하는 방법을 학습했다.또한, 생성된 프로젝트를 GitHub에 연결하고, 기본적인 상수 클래스, 리포지토리 클래스, 엔티티 클래스를 생성하였다.불필요한 파일들이 추적되지 않도록 .gitignore 파일을 설정하였고, 이 작업은 gitignore.io에서 현재 개발 환경에 맞는 설정을 자동으로 생성하여 적용했다.추가로, application.properties 설정 파일을 application-default.yml과 application-docker.yml로 나누어 개발 서버와 운영 서버에서 각각 다른 환경 설정을 적용하였다. H2와 MySQL 중 하나를 선택하여 각 서버에 맞게 설정했다. 프로젝트의 뼈대 엔티티 개발하기설계한 테이블을 바탕으로 엔티티(Entity)를 개발했다. 기본적으로 BaseEntity와 프로젝트에 필요한 여러 엔티티를 작성하였다.JPA와 코틀린을 처음 접해 생소한 부분도 있었지만, 자바에 대한 기본 지식 덕분에 비교적 빠르게 적응할 수 있었다. 연관관계가 있는 엔티티와 없는 엔티티를 나누어 개발하면서 새로운 어노테이션도 많이 사용했는데, 강의에서 해당 어노테이션에 대한 설명이 명확해서 어려움 없이 따라갈 수 있었다. 미션 1 + 21:N테이블 설계하기, 깃허브 리포지토리 만들기1주차에서 배운 내용을 바탕으로 서브 프로젝트의 GitHub 리포지토리를 만들고 테이블을 설계하는 미션을 수행했다.GitHub 리포지토리 생성은 문제없이 따라갔으나, 테이블 설계에서는 과연 내가 올바르게 하고 있는지 의구심이 들었다.적은 수의 테이블을 설계하는 것조차 여러 요인을 고려해야 했고, 설계한 테이블들이 적합한지 확신이 서지 않았다.우선 테이블을 설계하고, 프로젝트를 진행하면서 고려하지 못했던 부분이나 잘못 설계된 부분은 수정해나가기로 생각하였고, 이 과정에서 배운 점들을 기록하며 앞으로 개선해나갈 예정이다.https://github.com/Malvin222/mission-backoffice 

웹 개발백엔드웹개발워밍업클럽스프링

vin 2024.10.05
미스터디벨로

[이카운트] ECOUNT API 연동(5)

API 제공 기능 테스트 ( PHP CURL )판매 입력(품목)  - 문서 ( 너무 길어서 생략 ) - PHP CODE/* 판매 입력 */ /* 문서 TEST URL = https://sboapi{ZONE}.ecount.com/OAPI/V2/Sale/SaveSale?SESSION_ID={SESSION_ID} -> {ZONE}에 ZONE 조회 반환값 ZONE, {SESSION_ID}에 로그인 반환값 SESSION_ID */ $url = 'https://sboapiCC.ecount.com/OAPI/V2/Sale/SaveSale?SESSION_ID=로그인 반환값 SESSION_ID'; $arr_post['SaleList'] = array(); for($i=0;$i<5;$i++){ $data['SESSION_ID'] = '로그인 반환값 SESSION_ID'; $data['WH_CD'] = '100'; //창고 코드 $data['PROD_CD'] = '000100'; //품목 코드 $data['PROD_DES'] = '상품1'; //품목 명 $data['QTY'] = '3'; //수량 $child['BulkDatas'] = $data; $child['Line'] = $i.""; array_push($arr_post['SaleList'], $child); } $post_data = json_encode($arr_post); $ch=curl_init(); // user credencial curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json')); curl_setopt($ch, CURLOPT_VERBOSE, true); //POST방식 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true);※ 쇼핑몰 창고1 ( 창고코드 : 100 ) 에 상품1 ( 품목코드 : 000100 ) 을 3개씩 5번 팔았음  - 반환값※ 반환값 정상인지 확인하고 이카운트 ERP에서 판매현황, 재고 현황 다 확인 ㄱ

웹 개발미스터디벨로웹개발이카운트

내일배움단 웹개발종합반 학습 3일차

  08.22~ 08.25 사용 프로그램: 파이참 1. 작성해본 코드 1)loginPage. html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>로그인페이지</title> <link href="https://fonts.googleapis.com/css2?family=Song+Myung&display=swap" rel="stylesheet"> <link rel="stylesheet" type="text/css" href = "loginPage.css"></head><body> <div class="wrap"> <div class ="mytitle"> <h1>로그인페이지</h1> <h5>아이디, 비밀번호를 입력하세요</h5> </div> <p>ID: <input type="text"/></p> <p>PW: <input type="text"/></p> <button class="mybtn">로그인하기</button> </div></body></html> 2)loginPage.css .mytitle { color: white; width: 300px; height: 200px; text-align: center; background-image: url('https://www.ancient-origins.net/sites/default/files/field/image/Agesilaus-II-cover.jpg'); background-position: center; background-size: cover; /*백그라운드 관련 3가지 스타일: 덩어리 인식하면 편하다*/ border-radius: 10px; padding-top: 40px;}.mybtn { /**바깥 여백*/ margin: 20px 20px 20px 50px; /**안쪽 여백*/ padding: 40px;}.wrap { width: 300px; margin: auto; /*상하 좌우 최대로 미세요= 가운데*/}* { font-family: 'Song Myung', serif; /*구글 웹폰트 사용*/}/* 주석 단축키: Ctrl + / */ 3)index.html <!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <title>스파르타 피디아</title> <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet"> <style> * { font-family: 'Gowun Dodum', sans-serif; } .mytitle { background-color: green; width: 100%; height: 250px; background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url("https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg"); background-position: center; background-size: cover; color: white; display: flex; flex-direction: column; /*row colum으로만 바꿔사용*/ align-items: center; justify-content: center; /*덩어리 인식하면 편하다*/ } .mytitle > button { width: 200px; height: 50px; background-color: transparent; color: white; border-radius: 50px; border: 1px solid white; margin-top: 10px; } .mytitle > button:hover { border: 2px solid white; } .mycomment { color: green; } .wrap { margin: 20px auto 0px auto; max-width: 1200px; width:95%; } .mypost { width:95%; max-width: 500px; margin: 20px auto 0px auto; box-shadow: 0px 0px 3px 0px gray; padding: 20px; } .buttons { display: flex; flex-direction: row; /*row colum으로만 바꿔사용*/ align-items: center; justify-content: center; /*덩어리 인식하면 편하다*/ margin-top: 20px; } .buttons > button { margin-right: 10px; } #button1 { background-color: black; color: white; max-width: 80px; width:95%; padding:5px; border: 1px solid black; border-radius: 5px; } #button2 { background-color: white; color: black; max-width: 50px; width:95%; padding:5px; border: 1px solid black; border-radius: 5px; } </style></head> <body> <div class="mytitle"> <h1> 내 생애 최고의 영화들 </h1> <button> 영화 기록하기</button> </div> <div class = "mypost"> <div class="form-floating mb-3"> <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com"> <label for="floatingInput">영화 URL</label> </div> <div class="input-group mb-3"> <label class="input-group-text" for="inputGroupSelect01">별점</label> <select class="form-select" id="inputGroupSelect01"> <option selected>--선택하기--</option> <option value="1">⭐</option> <option value="2">⭐⭐</option> <option value="3">⭐⭐⭐</option> <option value="4">⭐⭐⭐⭐</option> <option value="5">⭐⭐⭐⭐⭐</option> </select> </div> <div class="form-floating"> <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea" style = "height:100px"></textarea> <label for="floatingTextarea">코멘트</label> </div> <div class = "buttons"> <button id = "button1">기록하기</button> <button id = "button2">닫기</button> </div> </div> <div class="wrap"> <div class="row row-cols-1 row-cols-md-4 g-4"> <div class="col"> <div class="card"> <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">여기에 제목이 들어갑니다</h5> <p class="card-text">여기에 내용이 들어갑니다</p> <p>⭐⭐⭐</p> <p class="mycomment">여기에 코멘트가 들어갑니다</p> </div> </div> </div> <div class="col"> <div class="card"> <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">여기에 제목이 들어갑니다</h5> <p class="card-text">여기에 내용이 들어갑니다</p> <p>⭐⭐⭐</p> <p class="mycomment">여기에 코멘트가 들어갑니다</p> </div> </div> </div> <div class="col"> <div class="card"> <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">여기에 제목이 들어갑니다</h5> <p class="card-text">여기에 내용이 들어갑니다</p> <p>⭐⭐⭐</p> <p class="mycomment">여기에 코멘트가 들어갑니다</p> </div> </div> </div> <div class="col"> <div class="card"> <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">여기에 제목이 들어갑니다</h5> <p class="card-text">여기에 내용이 들어갑니다</p> <p>⭐⭐⭐</p> <p class="mycomment">여기에 코멘트가 들어갑니다</p> </div> </div> </div> </div> </div> </body></html> 2. 스터디 출석여부: 출석완료   3. 강의내용 중 노트 필기와 간단 소감 HTML/CSS **주석 단축키 : Ctrl + / CSS 사용시 부트스트랩 활용 *유용한 덩어리: 백그라운드, 디스플레이 모음 class 선택자 하위 단락 특정하여 속성을 부여하고 싶을 때: ex) .mytiltle > button:hover {} 모바일 모드 미리보기: 개발자모드   아직까지는 큰 어려움 없이 진행중.. 다만 게으름과의 싸움! 정신줄을 잡는 데 필요한 건 습관화.        

웹 개발웹개발

채널톡 아이콘