블로그

한지은

[인프런 워밍업 스터디 클럽 1기] FE 2주차 발자국

day6 - 6번 과제https://github.com/jjajan2/inflearn_study/tree/main/day6_password_generator처음에는 아스키코드 번호를 이용해서 만들려고했지만 특수문자의 경우 각각 적어줘야되기 때문에 한번에 문자열을 넣는 방식을 선택했다. range를 사용할 일이 많이 없어서 거의 처음으로 다뤄보았는데, 스타일의 경우 따로 div를 넣어서 변경해주는 방식으로 하는것같았다. 이 부분은 나중에 추가적으로 보고 적용해봐야겠다!day7 - 7번 과제https://github.com/jjajan2/inflearn_study/tree/main/day7_typing_speed7가지의 과제를 하면서 타이밍 스피트 테스트 과제가 생각보다 어렵게 느껴졌다. 한 문장을 완성하고 다음문장으로 넘어갈때 Error와 Accuracy가 누적되지않고 0으로 초기화되는 현상이 있었는데, totalErrors와 totalAccuracy를 추가해서 한 문장이 끝날때마다 누적하는 방식으로 해결했다! 생각해보면 별거 아니었던것같은데 바로 생각해내지 못해서 아쉽다.그리고 7번 타이핑 스피드 과제와 3번 퀴즈 과제에서 json파일로 데이터를 따로 빼서 사용했는데, 과제를 하던 도중 콘솔창에 에러가 생기는걸 발견했다.이 에러 메시지는 import 구문에서 assert 옵션을 사용하는 것이 더 이상 권장되지 않으며, 향후 V8 v12.6과 Chrome 126부터 지원이 중단될 것이라는 내용이고, 과거에 정적 분석을 위해 사용되었다고 한다. 이를 대신해 with을 사용하도록 권장되고있다. 라는것을 알게되었다. 하지만 fetch를 사용하는 방식으로 변경했다. 7번과제를 할때까지 몰랐던거 보면 3번과제를 할때 콘솔창을 제대로 확인하지 않았던것 같다ㅠㅠ 에러를 꼼꼼히 확인하는 습관을 기르자..!그리고 타이머를 체크하는 부분을 setTimeout과 반복문을 사용해서 만들었는데, 다 만들고나니까 코드가 허술하다는 생각이 들었다.. 그래서 setInterval을 사용하는 방식으로 변경했다!day9 - 8번 과제https://github.com/jjajan2/inflearn_study/tree/main/day9_budget_calculator예산 계산기 과제를 스터디 팀원분들과 함께 코드리뷰를 했다!총 합계를 계산하는 부분을 map을 사용해서 계산하는 코드를 작성했는데, 팀원분이 reduce를 사용하지 않은 이유를 물어보셨다..! 사실 reduce를 생각하지 못했어서 바로 reduce를 사용하는 방식으로 변경했다. (감사합니다! 😊)나도 팀원분들의 코드를 보면서 피드백을 해드리고 싶었는데, 아직 실력이 부족해서 딱히 해드릴수 있는게 없었다..확실한건 다른사람이 작성한 코드를 보는건 많은 도움이 되는것같다!인프런 스터디가 끝나도 다른 여러가지 스터디, 프로젝트를 해보면서 실력을 많이 키워야겠다

프론트엔드워밍업클럽FE1기

슬프구나

인프런 워밍업 클럽 스터디 1기 FE - 2주차 발자국

2주차는 드디어 본격적으로 리액트와 Next.js 를 하는 주간이었다! useState()useEffect()useLayoutEffect()useRef()useMemo()useCallback()useContext()useReducer()등에 다양한 훅을 복습 하였다. 리액트 훅은 16.8 에 추가가된 기능이다. 등장하면서, 클래스 컴포넌트의 시대는 저물었다. 리액트 훅 함수는 반드시 함수 컴포넌트에서만 사용해야만 한다.리액트 훅은 조건에 또는 반복문에 따라 호출이 되면 안된다.useState() 는 함수 컴포넌트내에서 상태를 관리하는 훅이다.useEffect(), useLayoutEffect 는 함수 생명주기에 대응하는 훅이다.2번째 인자로 배열을 가지는(의존성 배열) 훅들은 사용할 때 항상 주의해야한다. -> 안그러면 클로저로 인해 이전 값을 계속 참조한다.useEffect() 훅은 함수 컴포넌트가 반환하는 JSX 구문 즉 ReactElement 가 실제DOM에 렌더링 된 후 paint 후에 비동기로 동작하는 반면 useLayoutEffect() 훅은 동기적으로 페인트 이전에 실행이 된다. 리액트 함수 컴포넌트 생애주기는Render 단계와 Commit 단계로 나뉜다.Render 단계에서는 함수가 실행되어, JSX 를 반환한다. JSX 는 ReactElement 를 생성하는 함수로 변환이 되어 ReactElement 를 생성한다. 이걸 가지고 가상 DOM을 만든다. 이전에 만든 가상DOM이 있다면 이를 비교하는 작업을 한다.Commit 단계에서는 가상DOM을 실제 DOM에 반영한다.  Next.js는 React 프레임워크를 기반으로 한 웹 개발 도구로, 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 그리고 최근에는 클라이언트 사이드 렌더링(CSR)과 같은 다양한 렌더링 방식을 지원합니다. 각 렌더링 방식에 대한 설명과 특징을 살펴보겠습니다. 서버 사이드 렌더링 (Server-Side Rendering, SSR)개념: SSR은 각 요청이 있을 때마다 서버에서 HTML을 동적으로 생성하여 클라이언트에게 전송하는 방식입니다. 이는 초기 페이지 로딩 시 서버에서 모든 페이지를 미리 렌더링하고, 완성된 HTML을 클라이언트에게 보내줍니다.장점: 검색 엔진 최적화(SEO)에 유리하고, 초기 로딩 시 사용자에게 완성된 페이지를 보여줄 수 있어 사용자 경험이 개선됩니다.단점: 서버 부하가 늘어날 수 있으며, 렌더링 시간이 길어질 수 있습니다.정적 사이트 생성 (Static Site Generation, SSG)개념: 빌드 시 모든 페이지를 미리 HTML로 변환하여 저장합니다. 사용자의 요청에 따라 미리 생성된 HTML 파일을 그대로 전송합니다.장점: 서버 부하가 감소하고, 빠른 로딩 속도를 제공합니다. 보안이 강화되며, CDN을 통한 쉬운 배포가 가능합니다.단점: 실시간 업데이트가 필요한 경우 적합하지 않을 수 있습니다. 사이트 빌드 시간이 길어질 수 있습니다.클라이언트 사이드 렌더링 (Client-Side Rendering, CSR)개념: 초기 로딩에서는 기본적인 HTML과 JavaScript를 로드하고, 이후 모든 렌더링은 브라우저에서 JavaScript를 통해 이루어집니다.장점: 서버 부하가 줄어들고, 사용자 인터랙션에 따라 동적인 페이지 변화를 빠르게 구현할 수 있습니다.단점: 초기 로딩 시 필요한 자원이 많아져서 속도가 느려질 수 있으며, SEO에 불리할 수 있습니다.증분형 정적 재생(Incremental Static Regeneration, ISR)개념: ISR을 사용하면, 빌드 시 생성된 정적 페이지를 배포 후에도 필요에 따라 업데이트할 수 있습니다. 이는 특정 페이지를 새로운 데이터로 다시 생성하게 할 수 있는 옵션을 제공하여, 정적 사이트의 장점을 유지하면서도 동적 콘텐츠의 필요성을 충족시킬 수 있습니다. 장점성능과 캐싱: 정적 파일로 서빙되기 때문에 빠르고, CDN을 통해 캐싱될 수 있어 성능이 우수합니다.스케일링: 정적 파일을 사용하기 때문에 트래픽 증가에 따른 서버 부하가 적습니다.신선도: revalidate 옵션을 통해 정적 콘텐츠임에도 불구하고 일정 기간마다 콘텐츠를 자동으로 업데이트하여 최신 상태를 유지할 수 있습니다.단점복잡성: ISR 설정과 관리는 일반적인 SSG나 SSR에 비해 복잡할 수 있습니다.비용: 재생성 로직에 따라 추가 서버 자원이 필요할 수 있으며, 이로 인해 비용이 발생할 수 있습니다.Next.js 에서는 개발자를 위해 다양한 기능을 제공해 주고 있다.파일 기반 라우팅api 라우팅Image 최적화Metadata API등 다양해서 좋다. 그런데 프로젝트를 작성해나가다보면 예약어 파일들이 너무 많아져서 큰 회사들은 이걸 어떻게 관리하는지 궁금해진다.

프론트엔드인프런워밍업클럽FE1기회고

강나현

[1주차] 인프런 워밍업 클럽 스터디 FE 1기 발자국

강의 수강1) 일주일 간 학습한 내용이전에 접했던 개념이지만 다시 봐도 헷갈렸던 개념, 몰랐던 개념 위주로 정리해보았다.const vs let vs varconst , let → 블록 레벨 스코프function func(){ if(true){ let a = 0; } console.log(a) // reference error } var → 함수 레벨 스코프function func(){ if(true){ var a = 0; } console.log(a) // a 출력 } 호이스팅- 코드 실행 전 변수나 함수 선언을 맨 위로 끌어올려지는 것을 의미함.// 1. var 변수 생성시 undefined로 선언 후, hello 할당 console.log(greeting); //undefined 출력 var greeting = 'hello'; // 2. let, const 변수 생성 시 // 호이스팅은 발생하지만 초기에 초기화되지 않고 값을 할당하기 전에 콘솔 로그가 발생함. // 발생 원인: TDZ(Temporal Dead Zone) // → 선언단계와 할당 단계 사이 변수를 사용할 수 없는 일시적 비활성 상태 console.log(greeting); // reference error let greeting = 'hello';  최대한 let보다 const를 사용하되 let 사용시 scope를 좁게 만들어서 사용하자.for vs forEachfor가 forEach 보다 성능이 좋음.따라서 복잡한 코드인 경우에는 for가 좋지만 가독성 측면에서는 forEach가 좋을 수 있음.for는 await과 함께 동작하지만 forEach는 await과 완벽하게 동작하지 않음.DOMDOM vs BOMDOM(Document Object Model)- DOM은 웹 브라우저가 HTML 페이지를 인식하게 해주는 트리구조로 된 객체 모델을 의미함.- JavaScript와 html을 연결해주는 역할BOM(Browser Object Model)- JavaScript가 브라우저와 소통하는 역할- window객체를 제어함.DOM 동작방식브라우저가 서버에서 페이지에 대한 응답을 받아 화면에 표시하는 단계1. DOM 트리 생성2. Render 트리 생성 - 브라우저가 DOM과 CSSOM(javascript가 css를 조작할 수 있는 api)을 결합하는 과정으로 화면에 표시되는 노드의 정보, 스타일 정보를 포함3. Layout - 브라우저 요소의 크기와 위치 계산 4. Paint - 실제 화면에 렌더링Document Object 이용document.getElementsByTagName으로 불러온 노드를 배열로 변환하는 방법let li = document.getElementsByTagName('li'); //collection으로 출력 li = Array.from(li); console.log(li); // 배열로 출력innerHTMLhtml까지 같이 보여줌.innerText 사용자에게 보여지는 화면 그대로 보여줌.(실제 코드에서 공백이 여러개이지만 1개 공백으로 처리)textContentdisplay:none과 같이 숨겨진 노드도 출력하고 텍스트 값 그대로 보여줌.childNodes 또한 collection 이며 collection의 특징은 아래와 같음.collection을 순환할때 for…of, forEach() 사용가능하며 for…in 은 사용 불가능함.collection은 배열이 아니기 때문에 filter와 같은 배열 메서드 사용 불가함. eventeventBubbling이벤트가 발생했을 때 중첩된 상위 요소로 이벤트가 전달되는 현상e.stopPropagation()으로 해당 현상을 막을 수 있음.eventCapturing이벤트가 아래 요소로 전달되는 현상preventDefault()별도의 브라우저 행위(ex. submit 태그 실행 시 화면이 새로고침 되는 현상)를 막을 수 있음.2) 학습 내용에 대한 회고연휴 겸 여행을 가게 되어 ktx 열차 안에서 틈틈이 강의를 들었다. 덕분에 진도를 따라잡을 수 있었지만 꼼꼼하게 내용을 기록하지 못해 아쉬웠다. 또한, 과제를 더 완벽하게 해낼 시간이 부족했기 때문에 2주차는 스터디에 시간을 더 소비해야겠다는 생각이 들었다. 미션1) 미션 해결 과정문제 현상가위 바위 보 앱을 구현하며 다시 시작 버튼을 누르고 가위 바위 보 버튼 중 하나를 클릭하면 게임 총 횟수가 2개씩 줄어드는 현상이 발생했다.문제점콘솔로 가위 바위 보 버튼 이벤트 타겟을 찍어보니 클릭은 한 번만 했지만 이벤트 타겟은 2개가 찍히는 문제가 있었다.원인처음엔 버튼을 감싸고 있는 부모태그까지 이벤트가 전달되는 '버블링 현상' 인줄 알고 e.stopPropagation() 을 적용해보았지만 해결이 되지 않았다. 구글링을 통해 익명함수로 이벤트 리스너를 사용하면 새로운 객체로 생성되어 중복이 발생한다는 점을 알게되었다.해결방안익명함수 대신 선언적 함수로 코드를 수정하니 클릭 이벤트가 한번만 발생했다. 2) 미션 해결에 대한 회고음식 메뉴 앱반응형 레이아웃을 적용해보려고 노력했지만 의도와 다르게 정렬이 흐트러졌다. 해당 과제를 통해 css 지식이 많이 부족하다고 느꼈고 추후에 css 개념을 공부해서 수정해야겠다는 생각이 들었다. 지금은 JavaScript 공부에 더 집중해야겠다.2. 가위 바위 보 앱, 퀴즈 앱가위 바위 보 앱을 구현하며 이벤트 중복 현상을 해결하기 위해 시간을 많이 소비했고 처음부터 설계를 제대로 못해서 스파게티 코드가 된 것 같다. 평소에 토이 프로젝트로 프론트엔드 개발 역량을 많이 쌓아야겠다.3. 퀴즈 앱이전 과제들의 부족한 점을 생각하며 init() 함수에 미리 구현해야할 함수명들을 적어놓고 시작했다. 하지만 중간에 버튼을 변경하는 부분에서 코드가 꼬이기 시작하더니 마찬가지로 코드가 복잡해졌다. retry, next 버튼을 변경하는 부분에서 display: none으로 스타일링에 변화를 줘야할지 innerText로 버튼명을 변경해야할지 고민을 했던 것 같다. 정답 코드가 나오면 더 효율적인 코드가 무엇인지 체크해봐야겠다.  

프론트엔드

강나현

[2주차] 인프런 워밍업 클럽 스터디 FE 1기 발자국

강의 수강1) 일주일 간 학습한 내용이번주도 마찬가지로 이전에 접했던 개념이지만 다시 봐도 헷갈렸던 개념, 몰랐던 개념 위주로 정리해보았다.symbol()유니크한 식별자를 만들기위해 사용함.객체를 for in으로 추출할때 symbol 식별자는 통과하지 않음.  Generator 함수사용자의 요구에 따라 다른 시간 간격으로 여러 값을 반환 function* generatorFunc(){ yield 1; //return 1과 같은 의미 yield 2; yield 3; } const number = generatorFunc(); console.log(number.next()); //1 console.log(number.next()); //2 console.log(number.next()); //3 //아래 2개는 같은 의미 const generator = generatorFuncion(); const generator = generator[Symbol.iterator](); 디자인 패턴Singletone design pattern 각각의 다른 객체를 하나의 객체로 제한하는 패턴Factory design pattern 비슷한 객체를 반복적으로 만들어야하는 상황에 이용하는 패턴Mediator Pattern 채팅방 예시) 채팅방에 입장후 메시지를 보낼때 중재자를 통해서 메세지를 보내는 패턴Observer Pattern subject를 관찰하는 observer들이 있을 때 관심있는 주제를 구독하고 취소하는 Publisher(게시자)-Subscriber(구독자) 관계 패턴Module Pattern 코드를 최대한 분할하여 모듈화하는 패턴프로젝트 개발setInterval(startTimer,10); 1000ms 가 1초이면 10ms 간격으로 startTimer 함수 실행<React>프레임워크 vs 라이브러리프레임워크 : 앱을 만들기 위해 필요한 대부분의 라이브러리를 포함하고 있음.라이브러리 : 특정 기능을 모듈화 해놓은 것.프레임워크는 라이브러리를 포함하고 우리가 작성한 코드를 호출함. 또한 코드는 기능 구현을 위해 라이브러리를 호출하는 관계를 가짐.Node.js웹 브라우저 환경이 아닌 곳에서도 자바스크립트로 연산가능하게 하는 자바스크립트 런타임.리액트 설치 시 필요한 이유: 프로젝트를 개발하는데 필요한 도구들이 Node.js를 사용하기 때문 ex) 주요 개발 도구 - 바벨 : 최신 js 문법을 지원하지 않는 브라우저에서도 최신 js문법이 돌아갈수 있도록 변환해주는 라이브러리 - 웹팩 : 모듈화된 코드를 하나의 js코드로 압축하는 라이브러리 - npx: 노드 패키지 실행을 도와주는 도구가상 돔 virtual dom 등장 배경웹페이지 빌드 과정에서 dom에 변화가 발생하면 render tree재성성 → layout → repaint 의 과정을 반복함. 인터렉션이 많아지면 성능상의 문제를 초래하기 때문에 실제 dom을 메모리에 복사해준 virtual dom이 등장함.가상 돔은 브라우저 문서에 직접 접근하여 화면 요소를 수정할 수 없음.가상 돔 virtual dom 작동방식랜더링 이전 가상돔과 바뀐 가상 돔을 비교 → Diffing바뀐 부분만 실제 돔에 적용시켜주는 것 → reconciliation (재조정)재조정 과정에서 바뀐 state가 n개라면 n번 조작하는 것이 아닌 한번의 dom조작으로 변화를 반영함. → batch update리액트 기본구조webpack 은 src내 파일만 처리함.전개 연산자객체const obj1 = { a: 'A' }; const obj2 = { b: 'B' }; const objWrap = {obj1,obj2}; console.log(objWrap) /*{ obj1:{ a: 'A' }, obj2:{ b: 'B' } }*/ const objWrap2 = {...obj1, ...obj2}; console.log(objWrap2); /* { a: 'A', b: 'B' } */ SPASPA에서 페이지 전환(브라우징)은 html5의 history api를 사용해서 가능하게 함.history.back() : 브라우저의 뒤로가기 효과history.go(): 특정 세션기록으로 이동하게 해주는 비동기 메서드. 1을 넣으면 바로 앞페이지/-1을 넣으면 바로 뒤페이지로 이동history.pushState(): 주어진 데이터를 세션 스택에 넣음.React Hooksclass없이 state를 사용할수 있는 기능TailWindCSShtml안에서 css스타일을 만들게 해주는 css 프레임워크빠른 스타일링이 가능하며 class혹은 id명을 작성하지 않아도 됨.리액트 불변성배열과 객체 같은 참조 타입은 불변성을 가지고 있지 않음.(mutable)리액트에서 화면을 업데이트할때마다 변경된 부분을 확인해야하므로 불변성을 지켜줘야함. → spread 연산자, map, filter, slice, reduce 활용(splice, push 는 원본 데이터 변경함.) useDebouncedebounce란 검색 입력에 입력결과가 나타날때까지 지연이 있는 기능위 기능을 사용하지 않으면 입력한 모든 문자를 처리해서 성능이 저하됨.2) 학습 내용에 대한 회고그동안 React로 토이 프로젝트를 하면서 겉핡기식으로만 알고 있었다는 것을 절실히 깨달았다. 이번주 강의를 수강하며 성능을 고도화시키기 위해 hooks를 만들어 사용하는 점을 배웠다. 아직 부족하지만 앞으로 사이드 프로젝트를 하면서 적용해봐야겠다.미션1) 미션 해결 과정문제 현상예산 계산기 앱을 구현하며 예상치 못한 문제가 발생했다. 예산 데이터 리스트 Lists 컴포넌트 아래 각각의 데이터를 나타내는 List 하위 컴포넌트를 구현했다. 하지만 List 컴포넌트가 화면에 보이지 않았고 에러 메시지도 없었기 때문에 당황스러웠다.원인List 컴포넌트를 map 함수로 표기하는 과정에서 JSX 반환코드를 {} 안에서 사용한 것이 문제였다. 이러한 경우에는 return 키워드로 반환하거나 ()로 감싸서 반환해야한다. 해결방안//변경 전 코드 const Lists = ({ budgetData }) => { return ( <div> {budgetData.map((data) => { <List key={data.id} id={data.id} title={data.title} price={data.price} />; })} </div> ); }; //변경 후 코드 const Lists = ({ budgetData }) => { return ( <div> {budgetData.map((data) => ( <List key={data.id} id={data.id} title={data.title} price={data.price} /> ))} </div> ); };  2) 미션 해결에 대한 회고타이핑 테스트 앱wpm, cpm 등 각각의 기능을 계산하는 방법을 파악하는데 시간이 꽤 소요됐다. 또한, 글자를 타이핑하면서 실시간으로 맞은문자, 틀린 문자를 초록색, 빨간색으로 변경하는 방법도 고민됐었다. 다른 사람들에겐 쉬운 과제였을수 있겠지만 나에게는 생각보다 고려해야할 부분이 많았던 과제였다.2. 예산 계산기 앱오랜만에 리액트 앱으로 개발을 하게되어 떨리는 마음으로 과제를 시작했다. 중간점검 때 강사님께서 어느정도 관습성을 가진 코드가 좋은 코드라 했던 말이 떠올랐다. 그래서 강의에서 다뤘던 내용을 중심으로 따라가다보니 재미를 느끼며 개발을 할 수 있었다. 물론, 모방이 주가 아닌 본인만의 색깔을 가져야 한다고 하셨으니 나만의 색을 가진 코드를 짤 수 있도록 개발 역량을 쌓아야겠다.3. 디즈니 플러스 앱아직 구현할 기능들이 조금 남아있지만 확실히 swiper와 같이 라이브러리를 추가하면서 겪는 문제들이 많아졌다. 그러다보니 시간이 더 오래걸리는 것 같다. 그래도 끝까지 마무리하는 습관을 가져야지!👏

프론트엔드

홍승찬

[인프런 워밍업 클럽 스터디 BE 1기] 두 번째 발자국

두 번째 발자국배운것들스프링 컨테이너의 의미와 사용 방법국비교육에서 이미 스프링 컨테이너에 대해 배웠었기 때문에 어느 정도 알고 있었는데, 강사님의 상세한 강의로 모르고 있었던 개념들과 헷갈렸던 부분들을 확실하게 잡고 갈 수 있었다. 특히 "@bean 어노테이션의 경우 메서드에 사용하고, @Component어노테이션은 클래스에 사용한다" 라는 것만 알고 있었는데, 기존에 이미 사용하고 있었던 어노테이션들도 @Component를 사용한 것이 었다는 것을 알고 더 깊게 이해할 수 있었다.  Spring Data JPA를 사용한 데이터베이스 조작이제까지 MyBatis만 사용하고 SQLMapping기술만 사용해서 어떻게 sql을 쓰지 않고 DB를 조작할 수 있는지 의문이었는데, 강의를 듣고 JPA에 대해 알아가면서 SQLMapping기술과는 너무 달라서 충격이었다. 단지 JpaRepository를 상속받은 인터페이스를 만든 것 만으로 SQL없이 DB를 조작하는 것을 보고 너무 생소해서 필기하면서 공부하고 복습이 필요하겠다는 것을 느꼈다. 그리고 이제까지 MyBatis처럼 JPA가 하나의 라이브러리인줄 알았는데, JPA는 하나의 규칙이라는 것을 새롭게 알게 됐다. JPA의 구현체 중 하나가 Hibernate이며 이 전체를 쓰기 쉽게 해주는 것이 Spring Data JPA 라이브러리이다. 라는 부분을 들으면서 JPA에 대해 하나도 몰랐구나를 알게 됐다.. 트랜잭션과 영속성 컨텍스트JPA를 사용하면서 가장 흥미로웠던 부분이 Update부분이다. 기존 자료를 수정하기 위해서는 Update 쿼리를 날려줘야하지만 JPA에서는 도메인 내부에서 데이터 수정함수를 만들면 기존 데이터에서 바뀐 데이터를 감지하고 update문을 알아서 날려준다는 것이다. 이 부분이 가능했던 이유가 영속성 컨텍스트 라는 것의 변경감지 기능 덕분이다. 이 외에도 쓰기지연(여러 sql문이 commit될 때 한번에 저장됨/ DB커넥션비용을 절감), 1차 캐싱(ID를 기준으로 엔티티를 기억)등이 있다!회고저번주까지는 알고 있던 내용들이었기에 복습하는 과정이었다면, 이번주는 JPA를 다루기 시작하면서 새롭게 배우는 것들이 훨씬 많아졌다. 그중에서도 JPA 직접 다뤄보면서 SQL을 직접 만들어서 쓰던 것과 다르게 신세계를 경험할 수 있었다. 이제까지 따로 필기는 하지 않으면서 과제 정도만 진행했었는데, 배우는 내용이 많아지면서 필기를 하지 않으면 까먹겠다는 생각이 들어 뒤늦게 블로그에 배운 내용들을 올리고 있다. 이번주 금요일에 깜짝 라이브 방송이 있었는데, 까먹고 참여하지 못해서 너무 아쉬웠다. 다음주 금요일은 진짜 꼭 참여해야겠다. 

백엔드인프런워밍업클럽스터디1기워밍업클럽스터디1기워밍업클럽1기워밍업클럽BE

전재민

[인프런 워밍업 스터디 클럽 1기_디자인] 두번째 발자국

두번째 발자국배운 내용Inputbutton, checkbox, radio button, switch, labeltext field, text area, selected Displayavatar, accodion, badge, tooltips, dividerchip, card, table  5/7 8일차component 파트의 첫날button - 일반적인 버튼checkbox - 누르면 체크 표시가 뜨는 네모난 버튼radio button - 동그란 버튼switch - 스위치 버튼label - 버튼이나 다른 곳에 자주 쓰이는 텍스트이름 정해주고 properties 세팅하는게 서툴러서 힘들었지만 결과를 보니 뿌듯했다,5/8 9일차갑자기 몸살 감기가 걸려서 하루 밀리게 됨 5/9 10일차텍스트 박스 같이 글자를 입력할 수 있는 ui를 만들었다.text field - 일반적인 텍스트 입력할수 있는 바text area - text field보다 더 넓은 공간selected - 라디오, 체크박스가 선택되었을때 달라지는 ui대부분이 default, hover, press, focus, disabled, error 순으로 가서 점점 적응이 빨라졌다.그리고 이전에 등록했던 컬러 베리어블을 사용하면서 미리 등록하면 정말 편하다는걸 느낌.5/10 11일차display 컴포넌트를 시작한 날.처음 봤을 땐 간단해 보였지만 막상 해보니 제일 시간이 오래 걸렸던 날이다.avatar - 텍스트, 사진, 아이콘으로 만든 프로필accodion - 접고 펼 수 있는 컴포넌트badge - 카톡 알람 같이 오른쪽 상단에 있는 점, 999+tooltips - 메세지 같은 튜토리얼 바divider - 중간을 나누는 stroke이때 properties, 간격, 정렬 등 여러가지가 많이 꼬여버려 정리하는데 어려움을 느꼈다. 5/11 보충하루 밀린 과제를 마무리한 날하루씩 밀리면서 생각했던 건, 지금 커리큘럼의 하루 일정이 내가 소화할 수 있는 적절한 양이라는걸 알게됨chip - 간결하게 속성을 표시하는 uicard - 만능으로 쓰이는 카드 table - 헤더랑 셀로 만들어진 표개인적으로 table 만들때 쓴 한글더미 플러그인은 정말 마음에 들었다 느낀 점 + 아쉬운 점, 다음주는?전체적인 내용이 비슷비슷해서 초반은 좀 힘들었지만 후반에는 수월하게 진행된 주.중간에 아파서 하루가 밀렸는데 다시 따라잡기 힘들다는 걸 느낌.input에서 보너스 미션을 못한 게 아쉽다. -> 다음 주에 시간이 남으면 추가적으로 할 예정. 특히 컴포넌트를 만들고 결과물을 봤을 때 뿌듯함이 굉장히 컸었다. 다음 주는 밀리는 날 없이 또 빼먹는 거 없이 완벽하게 할 것이다  

UX/UI

손예지

[인프런 워밍업 클럽 스터디 1기] BE 2주차 <두 번째 회고록>

강의 요약Day 7 <스프링 컨테이너의 의미와 사용 방법>UserController와 스프링 컨테이너@RestControllerUserController 클래스를 스프링 빈으로 등록시킴스프링 빈서버가 시작되면 스프링 서버 내부에 거대한 컨테이너를 만들게 되는데 이 컨테이너 안에는 여러 클래스가 들어가게 됨이때 다양한 정보도 함께 들어 있고 인스턴스화도 이루어짐JdbcTemplate도 스프링 빈에 등록되어 있음미리 설정해 둔 의존성(Dependency)가 등록해 주고 있었음서버가 시작되면 일어나는 일스프링 컨테이너(클래스 저장소) 시작기본적으로 많은 스프링 빈 등록우리가 설정한 스프링 빈 등록이때 필요한 의존성 자동 설정UserRepository는 JdbcTemplate을 가져오지 못할까?JdbcTemplate을 가져오려면 UserRepository가 스프링 빈이어야 하는데 UserRepository는 스프링 빈이 아님UserRepository를 스프링 빈으로 등록하면 바로 가져올 수 있음UserService와 UserRepository를 스프링 빈으로 등록UserService -> @Service 어노테이션 추가UserRepository -> @Repository 어노테이션 추가이제 3개의 클래스가 서버를 시작할 때 따르는 순서가장 기본적인 스프링 빈 등록JdbcTemplate에 의존하는 UserRepository가 스프링 빈으로 등록UserRepository를 의존하는 UserService가 스프링 빈으로 등록UserService를 의존하는 UserController가 스프링 빈으로 등록스프링 컨테이너를 왜 사용할까?Repository를 다른 Class로 바꾸더라도 BookService를 변경하지 않는 방법Java의 Interface 활용@Primary 어노테이션이 붙어 있는 Repository를 우선 사용스프링 컨테이너의존성 주입(DI, Dependency Injection): 컨테이너가 선택해 BookService에 넣어 주는 과정제어의 역전(IoC, Inversion of Control) 방식스프링 컨테이너를 다루는 방법스프링 빈 등록하는 방법@Configuration클래스에 붙이는 어노테이션@Bean을 사용할 때 함께 사용해 주어야 함@Bean메소드에 붙이는 어노테이션메소드에 반환되는 객체를 스프링 빈에 등록언제 @Service, @Repository를 사용할까?개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때언제 @Configuration, @Bean을 사용할까?외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때@Component주어진 클래스를 '컴포넌트'로 간주이 클래슬들은 스프링 서버가 뜰 때 자동으로 감지@Component 덕분에 우리가 사용했던 어노테이션이 자동감지Controller, Service, Repository가 모두 아니고 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용스프링 빈 주입받는 방법생성자를 이용해 주입받는 방식 (권장)@Autowired 생략 가능 Setter와 @Autowired 사용누군가 setter를 사용하면 오작동할 가능성 있음필드에 직접 @Autowired 사용테스트를 어렵게 만드는 요인@Qualifier@Primary vs @Qualifier사용하는 쪽이 직접 적은 @Qualifier이 우선적Day 8 <Spring Data JPA를 사용한 데이터베이스 조작>문자열 SQL을 직접 사용하는 것이 너무 어렵다SQL을 직접 작성하면 아쉬운 점문자열을 작성하기 때문에 실수할 수 있고 실수를 인지하는 시점이 느림컴파일 시점에 발견되지 않고 런타임 시점에 발견됨특정 데이터베이스에 종속적이게 됨반복 작업이 많아지며 테이블을 하나 만들 때마다 CRUD 쿼리가 항상 필요데이터베이스의 테이블과 객체는 패러다임이 다름JPA(Java Persistence API)객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙Hibernate: 말로 되어 있는 규칙을 코드로 구현한 것Hibernate(구현체)이 JPA를 구현내부적으로 JDBC 사용유저 테이블에 대응되는 Entity Class 만들기Java 객체 <-> MySQL Table 매핑JPA 어노테이션@Entity: Spring이 User 객체와 user 테이블을 같은 것으로 바라봄 @Id: 이 필드를 primary key로 간주@GeneratedValue: primary key는 자동 생성되는 값임@Column: 객체의 필드와 Table 필드 매핑JPA를 사용하기 위해서는 기본 생성자가 반드시 필요JPA 추가 설정ddl_auto: 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지 (create, create-drop, update, validate, none)format_sql: SQL을 보여 줄 때 예쁘게 포맷팅 할 것인가show_sql: JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여 줄 것인가dialect: 조금씩 다른 SQL을 수정해 줌Spring Data JPA를 이용해 자동으로 쿼리 날리기JPA 기능 정리save: 주어지는 객체를 저장하거나 업데이트findAll: 주어지는 객체가 매핑된 테이블의 모든 데이터 가져옴findById: id를 기준으로 특정한 1개의 데이터 가져옴Spring Data JPA복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리Spring Data JPA를 이용해 다양한 쿼리 작성하기다양한 Spring Data JPA 쿼리By 앞에 들어갈 수 있는 구절 정리find: 1건을 가져옴, 반환 타입은 객체가 될 수도 있고 Optional<타입>이 될 수도 있음findAll: 쿼리의 결과물이 N개인 경우 사용, List<타입> 반환exists: 쿼리 결과가 존재하는지 확인, 반환 타입은 booleanBy 뒤에 들어갈 수 있는 기능 정리And / Or 조합 가능GreaterThan: 초과GreaterThanEqual: 이상LessThan: 미만LessThanEqual: 이하Between: 사이에StartsWith: ~로 시작하는EndsWith: ~로 끝나는Day 9 <트랜잭션과 영속성 컨텍스트>트랜잭션 이론편트랜잭션: 쪼갤 수 없는 업무의 최소 단위트랜잭션 시작: start transaction;트랜잭션 정상 종료: commit; (SQL 반영)트랜잭션 실패 처리: rollback; (SQL 미반영)영속성: 모든 SQL을 성공시키거나 하나라도 실패하면 모두 실패시키는 속성트랜잭션 적용과 영속성 컨텍스트@Transactional: 스프링에서 트랜잭션 적용할 때 사용주의사항: IOException과 같은 Checked Exception은 롤백이 일어나지 않음영속성 컨텍스트: 테이블과 매핑된 Entity 객체를 관리 및 보관하는 역할영속성 컨텍스트의 특수 능력변경 감지(Dirty Check): 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도 변경을 감지해 자동으로 저장쓰기 지연: DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라 트랜잭션이 commit 될 때 모아서 한 번만 날림1차 캐싱: ID를 기준으로 Entity를 기억함, 이렇게 캐싱된 객체는 완전히 동일Day 10 <조금 더 복잡한 기능을 API로 구성하기>책 생성 API 개발하기HTTP Method: POSTHTTP Path: /bookHTTP Body (JSON)결과 반환 X (Only HTTP 상태 200)대출 기능 개발하기HTTP Method: POSTHTTP Path: /book/loanHTTP Body (JSON)결과 반환 X (Only HTTP 상태 200)반납 기능 개발하기HTTP Method: PUTHTTP Path: /book/returnHTTP Body (JSON)결과 반환 X (Only HTTP 상태 200)회고1주차 스터디에 이어서 더 딥한 내용을 접하고 직접 사용해 볼 수 있었습니다. 아직 헷갈리는 부분이 있기는 하지만 강의를 반복해서 듣고 부족한 개념을 채워 넣는다면 배포까지 잘 마무리할 수 있을 것 같습니다. 미니 프로젝트까지 잘 완성하는 것이 목표입니다.

백엔드인프런워밍업클럽스터디발자국

손예지

[인프런 워밍업 클럽 스터디 1기] BE 1주차 <첫 번째 회고록>

강의 요약Day 2 <서버 개발을 위한 환경 설정 및 네트워크 기초>새로운 스프링 프로젝트 시작 방법spring initializer 접속Project: 이 프로젝트에서 사용될 ‘빌드 툴’ → 최근에는 Gradle 많이 사용Language: 서버를 개발할 때 사용할 언어 → 최신 프로젝트에는 Kotlin을 사용하는 경향이 있지만 Java로 만들어진 기존 프로젝트가 많이 존재Spring Boot: 스프링 부트의 버전을 고르는 항목 → 알파벳이 붙어 있으면 개발 중이거나 오픈 베타라는 의미, 시간이 지나면서 계속 버전이 업그레이드 되기 때문에 강의를 보는 시점에 따라 다른 숫자가 나올 수 있음버전은 첫 번째 숫자(major), 두 번째 숫자(month), 마지막 숫자(버그 및 간단한 기능 추가)Project MetadataGroup: 프로젝트 그룹Artifact: 최종 결과물 이름Name: 프로젝트 이름Description: 프로젝트 설명Package name: 패키지 이름Packaging: Spring Boot 톰캣이 내장되어 있어 Jar을 선택하면 됨Java: 기존에 존재하는 프로젝트는 Java11이 가장 많고 그 다음은 Java8이 많음 → 최신 프로젝트는 최신 Java 버전을 사용할 수 있음Dependencies: 프로젝트에서 사용하는 라이브러리 / 프레임워크라이브러리: 프로그래밍을 개발할 때 미리 만들어져 있는 기능을 가져다 사용하는 것프레임워크: 프로그래밍을 개발할 때 미리 만들어져 있는 구조에 코드를 가져다 끼워 넣는 것@SpringBootApplication과 서버어노테이션: Java의 문법으로 @ 뒤에 이런저런 문자열을 붙이는 것, 스프링을 실행하기 위해 필요한 다양한 설정들을 모두 자동으로 해 줌Server: 어떠한 기능을 제공하는 프로그램 (컴퓨터)서버와 요청: 기능을 제공하기 위해서는 누군가의 요청 필요네트워크란 무엇인가택배 시스템 <-> 네트워크집 <-> 컴퓨터주소 <-> IP주소 별칭 <-> 도메인 이름택배를 받는 사람 <-> port / 3000HTTP와 API란 무엇인가HTTP(HyperText Transfer Protocol)Protocol: 표준, 약속웹을 통한 컴퓨터 간의 통신은 HTTP라는 표준화된 방식이 있음HTTP MethodHTTP 요청은 HTTP Method(GET, POST)와 Path(/portion)가 핵심GET: HTTP 요청을 받는 컴퓨터에게 요청하는 행위 → 데이터를 달라POST: HTTP 요청을 받는 컴퓨터에게 요청하는 행위 → 저장하라 GET: 데이터를 달라, 쿼리DELETE: 데이터를 삭제하라, 쿼리POST: 데이터를 저장하라, 바디PUT: 데이터를 수정하라, 바디API(Application Programming Interface)클라이언트와 서버는 HTTP를 주고받으며 기능을 동작하는데 이때 정해진 규칙을 API라고 함정해진 약속을 하여 특정 기능을 수행하는 것 URL(Uniform Resource Locator)<http://spring.com:3000/portion?color=red&count=2>HTTP Status CodeHTTP/1.1 200 OK Content-Type: application/json { "name":"A", "age":null } 200 → OK300 → Moved Permanently400 → NotFound500 → Internal Server Error요청에서 응답은 상태 코드가 핵심GET API 개발 및 테스트API를 이루고 있는 요소HTTP MethodHTTP Path쿼리 (Key & Value)API의 반환 결과덧셈 API 생성HTTP Method: GETHTTP Path: /add쿼리: int number1 / int number2API의 반환 결과 -> 숫자 (두 숫자의 덧셈 결과)@RestController: 주어진 Class를 Controller로 등록, API의 입구@GetMapping("/add"): 함수를 HTTP Method가 GET이고 HTTP Path가 /add인 API로 지정@RequestParam: 주어지는 쿼리를 함수 파라미터에 넣음Day 3 <첫 HTTP API 개발>POST API 개발 및 테스트GET API에서 데이터 받기: 쿼리 이용POST API에서 데이터 받기: HTTP Body 이용JSON(JavaScript Object Notation): 객체 표기법, 무언가를 표기하기 위한 형식으로 중괄호가 양쪽에 있음곱셈 API 생성HTTP Method : POSTHTTP Path: /multiplyHTTP Body (JSON)API 반환 결과: 숫자 (곱셈 결과)유저 생성 API 개발유저 등록 APIHTTP Method: POSTHTTP Path: /userHTTP Body (JSON)결과 반환 X유저 조회 APIHTTP Method: GETHTTP Path: /user쿼리: 없음결과 반환 (JSON)Day 4 <기본적인 데이터베이스 사용법>Database와 MySQLDatabase: 데이터를 구조화 시켜 저장RDB(Relational Database) - MySQL: 데이터를 표처럼 구조화 시켜 저장SQL(Structured Query Language): 표처럼 구조화된 데이터를 조회하는 언어MySQL 접근 방법 -> MySQL Command List Client (무료)MySQL에서 데이터베이스 생성데이터베이스 생성create database [데이터베이스 이름];데이터베이스 목록 조회show databases;데이터베이스 삭제drop database [데이터베이스 이름];데이터베이스 안으로 접속use [데이터베이스 이름];테이블 생성create table 테이블 이름 ( ... );테이블 제거drop table [테이름 이름];MySQL Type정수: tinyint, int, bigint실수: double, decimal(A,B)문자열: char(A), varchar(A)날짜, 시간 타입: date, time, datetime테이블 데이터 조작C.R.U.DCreate, Retrieve or Read, Update, Delete데이터 넣기INSERT INTO [테이블 이름] (항목) VALUES (값);데이터 조회SELECT * FROM [테이블 이름];데이터 수정UPDATE [테이블 이름] SET 항목 = 값;데이터 삭제DELETE FROM [테이블 이름] WHERE 항목 = 값;Spring에서 Database 사용Spring <-> MySQL 연동을 위한 yaml 파일 작성spring: datasource: url: "jdbc:mysql://localhost/library" username: "root" password: driver-class-name: com.mysql.cj.jdbc.Driver이후 유저 테이블 생성, jdbcTemplate를 이용하여 POST API 변경, GET API 변경, 데이터 입력 및 조회Day 5 <데이터베이스를 사용해 만드는 API>유저 업데이트 API, 삭제 API 개발과 테스트유저 이름 업데이트HTTP Method: PUTHTTP Path: /userHTTP Body (JSON)결과 반환 X (HTTP Stauts 200)유저 삭제HTTP Method: DELETEHTTP Path: /user쿼리 사용: 문자열 name결과 반환 X유저 업데이트 API, 삭제 API 예외 처리하기SELECT문으로 해당 id나 name을 가진 사용자를 조회한 다음 그 사용자가 없을 시 IllegalArgumentException()으로 예외 처리Day 6 <클린코드의 개념과 첫 리팩토링>좋은 코드(Clean Code)는 왜 중요한가코드: 요구사항을 표현하는 언어개발자: 요구사항을 구현하기 위해 코드를 읽고 작성Controller에서 모든 기능을 구현하면 안 되는 이유함수는 최대한 작게 한 가지 일만 수행하도록 하는 것이 좋음클래스는 작아야 하며 하나의 책임만을 가져야 함Controller를 3단 분리하기Controller의 함수 1개가 하고 있던 역할API가 진입 지점으로써 HTTP Body 객체 변환 -> Controller 역할현재 유저가 있는지 없는지 확인하고 예외 처리 -> Service 역할SQL을 사용해 실제 Database와의 통신 담당 -> Repository 역할회고부트캠프를 수료한 지 오래되어서 JAVA의 대부분을 까먹었다고 봐도 무방한데 실무에 필요한 부분을 먼저 배우고 기초 지식을 채워나가는 것도 효율적인 방법이라 판단되어 스터디를 시작하게 되었습니다. 아직 알아야 할 것이 더 많지만 애매하게 알고 있었던 부분에 대해 상당히 많이 이해가 되어 신기했고 앞으로의 방향성에 대해서도 고려해 볼 수 있는 1주차였습니다.

백엔드인프런워밍업클럽스터디발자국

이혜리

[인프런 워밍업 클럽 스터디1기] 백엔드 - 6차 과제

진도표 6일차와 연결됩니다우리는 스프링 컨테이너의 개념을 배우고, 기존에 작성했던 Controller 코드를 3단 분리해보았습니다. 앞으로 API를 개발할 때는 이 계층에 맞게 각 코드가 작성되어야 합니다! 🙂과제 #4 에서 만들었던 API를 분리해보며, Controller - Service - Repository 계층에 익숙해져 봅시다! 👍   controller, service, repository로 분리해보자.  파일의 폴더 구조는 아래와 같다. controller, domain, dto, repository, service 폴더로 이루어져 있으며, 각각에 해당하는 파일들이 위치한다.  FruitController.javapackage com.group.libraryapp.controller.fruit; import com.group.libraryapp.domain.Fruit; import com.group.libraryapp.dto.fruit.FruitCreateRequest; import com.group.libraryapp.dto.fruit.FruitOverviewResponse; import com.group.libraryapp.service.FruitService; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.*; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @RestController public class FruitController { private final FruitService fruitService; public FruitController(FruitService fruitService) { this.fruitService = fruitService; } //문제1 @PostMapping("/api/v1/fruit") public void saveFruit(@RequestBody FruitCreateRequest request) { fruitService.saveFruit(request); } //문제2 @PutMapping("/api/v1/fruit") public void saledFruit(@RequestParam int id){ fruitService.saleFruit(id); } //문제3 @GetMapping("/api/v1/fruit/stat") public FruitOverviewResponse overviewFruit(@RequestParam String name){ return fruitService.overviewFruit(name); } } controller 파일은 심플하게 service의 메소드를 불러오는 방식으로 리팩토링했다.즉, 메소드만 명시하고, 실제적인 일은 service, controller가 한다는 뜻!위와 같이 코드를 짬으로서, api 의 진입점의 역할을 잘 하고 있다 볼 수 있다.FruitService.javapackage com.group.libraryapp.service; import com.group.libraryapp.domain.Fruit; import com.group.libraryapp.dto.fruit.FruitCreateRequest; import com.group.libraryapp.dto.fruit.FruitOverviewResponse; import com.group.libraryapp.repository.FruitRepository; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.util.List; @Service public class FruitService { private final FruitRepository fruitRepository; private FruitOverviewResponse fruitOverviewResponse; public FruitService(@Qualifier("sql") FruitRepository fruitRepository, FruitOverviewResponse fruitOverviewResponse) { this.fruitRepository = fruitRepository; this.fruitOverviewResponse = fruitOverviewResponse; } public void saveFruit(FruitCreateRequest request){ fruitRepository.saveFruit(request.getName(),request.getPrice(),request.getWarehousingDate()); } public void saleFruit(int id) { fruitRepository.saleFruit(id); } public FruitOverviewResponse overviewFruit(String name) { List<Fruit> list = fruitRepository.overviewFruit(name); long notsalesamount = 0; long salesamount = 0; for(Fruit e : list){ if (e.getSaled() == 1) notsalesamount += e.getPrice(); else salesamount += e.getPrice(); } fruitOverviewResponse.setNotSalesAmount(notsalesamount); fruitOverviewResponse.setSalesAmount(salesamount); return fruitOverviewResponse; } }FruitService는 FruitRepository의 메소드를 불러오는 역할과 동시에 유저가 있는지 없는지를 확인하고 예외처리를 하는 부분이다.위 클래스의 overviewFruit 함수에서선별한 정보를 아래와 같은 HTTP 응답 Body로 반환하기 위한 처리를 하도록 수정하였다. list 형태의 반환값을 FruitRepository의 메소드로 부터 받은 후, 이를 FruitOverviewResponse 객체로 반환하는 역할을 하고 있다.  FruitRepository.javapackage com.group.libraryapp.repository; import com.group.libraryapp.domain.Fruit; import java.time.LocalDate; import java.util.List; public interface FruitRepository { public void saveFruit(String name, long price, LocalDate warehousingDate); public void saleFruit(int id); public List<Fruit> overviewFruit(String name); }  문제2의 요구사항을 위해, repository를 바꿔가며 동작시킬 수 있도록 강의에서 이 경우에 interface를 작성하였기 때문에 interface 클래스를 작성하였다. @Primary 어노테이션 대신, @Qualifer 어노테이션을 FruitService 클래스에 사용했다.FruitMemoryRepository.javapackage com.group.libraryapp.repository; import com.group.libraryapp.domain.Fruit; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @Repository public class FruitMemoryRepository implements FruitRepository{ private final JdbcTemplate jdbcTemplate; private List<Fruit> memory = new ArrayList<>(); private int num = 0; public FruitMemoryRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void saveFruit(String name, long price, LocalDate warehousingDate) { memory.add(new Fruit(num+1,name, warehousingDate,price,1)); } public void saleFruit(int id){ for (Fruit e : memory){ if (e.getId() == id) e.setSaled(0); else throw new IllegalArgumentException(); } } public List<Fruit> overviewFruit(String name){ List<Fruit> list = new ArrayList<>(); for (Fruit e : memory){ if (e.getName().equals(name)){ list.add(e); }else throw new IllegalArgumentException(); } return list; } }위 클래스는 클래스 내에 List<Fruit> 를 만들어 저장함으로써, 프로그램을 재실행시키면, 기존 정보가 없어지는 repository이다. db와 연결이 없다.FruitMySqlRepository.javapackage com.group.libraryapp.repository; import com.group.libraryapp.domain.Fruit; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.time.LocalDate; import java.util.List; @Repository @Qualifier("sql") public class FruitMySqlRepository implements FruitRepository{ private final JdbcTemplate jdbcTemplate; public FruitMySqlRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void saveFruit(String name, long price, LocalDate warehousingDate) { String sql = "INSERT INTO fruits(name, warehousingDate, price,saled) Values (?,?,?,?)"; jdbcTemplate.update(sql, name, warehousingDate,price, 1); } public void saleFruit(int id){ String readSql = "SELECT * FROM fruits WHERE id = ?"; boolean fruitNotExist = jdbcTemplate.query(readSql, (rs,rowNum)-> 0,id).isEmpty(); if (fruitNotExist){ throw new IllegalArgumentException(); } //fruit exists String sql = "UPDATE fruits SET saled = ? WHERE id = ?"; jdbcTemplate.update(sql, 0,id); } public List<Fruit> overviewFruit(String name){ String readSql = "SELECT * FROM fruits WHERE name = ?"; List<Fruit> list = jdbcTemplate.query(readSql, (rs, rowNum) -> { String rs_name = rs.getString("name"); long rs_price = rs.getLong("price"); LocalDate rs_warehousingDate = rs.getDate("warehousingDate").toLocalDate(); int rs_saled = rs.getInt("saled"); return new Fruit(rs_name,rs_warehousingDate,rs_price,rs_saled); }, name); if (list.isEmpty()) throw new IllegalArgumentException(); return list; } }위 부분은 반대로 db와의 연결이 있고, 직접적인 sql 문을 사용하여 작성하였다. @Qualifier 어노테이션이 클래스 위에 붙어있는 것을 확인할 수 있다.회고controller 클래스만 쓰면, 여러 기능이 뭉쳐있어 코드의 가독성이 떨어지는데,위와 같이 3단 분리 및 interface 를 활용하니, 전보다는 클린코드로 작성이 된 것 같다.

백엔드워밍업1기6차과제백엔드

인프런 워밍업 클럽 1기 백엔드 1, 2주차 발자국

인프런 워밍업 클럽을 하게 됐다. 강사님의 이 강의를 전부터 듣고 싶었는데 이번에 들을 수도 있게 됐고, 정말 많은 경험이 되었다. 과제가 주어지는데, 나는 0기 때 한 관계로 이번 1기엔 과제가 뭔지 검색을 해서 찾아봐서 해야 했다. 이런 일이 있을거라고는 생각 못했기 때문에 문제가 어려운데 더해서 이 역시 뇌정지가 왔지만 별 수 없다는 생각이 들었다. 저번 기수에서는 잘하고 싶다고 해놓고 이런 저런 사정이 겹쳐서 손을 그냥 놔버렸었다. (과제 빼먹고 발자국 빼먹고 출석 체크 못하고 IDE가 문제가 생기고 등) 이번엔 잘하고 싶다.과제는 별로 쉽지 않았다. 가급적 간단하게 하려고 하는 편이고 그렇게 한다. 강사님의 예시를 볼 기회가 되어 보니 내가 뭔가 잘못 생각하고 있었구나 하는 생각이 들었다. 알던 얘기인데, 저렇게 적용되는구나, 하는 깨달음도 생기고 내가 어려워 하는게 이상한 일은 아니었으며 조급해 하지 말고 일단은 꾸준히 해나가야겠다는 생각이 들었다.그와 별개로 코틀린 역시 해보고 싶다. 전부터 코틀린을 써보고 싶었다.(리액트를 써보고 싶었다와 유사한 비중의 발언) 강사님께서 코틀린 강의를 하셨다니 그걸 들어봐야겠다. 언제가 될지는 모르겠지만, 꾸준히 해나가서 언젠가 도달할 것이다. 그렇게 하고 싶다.

DK

워밍업 클럽 1-2주차 후기 BE

워밍업 클럽을 시작하고 2주차입니다. 강의를 많이 듣지는 않았지만 성장하고 있다는 것을 느끼고 있습니다. 워밍업 클럽을 시작하기 약 한 달 전부터 자바/스프링 강의를 보며 따라왔지만, 내용이 너무 어려웠고 머릿속에 남는 것이 많지 않아 혼란스러워하고 있었습니다. 그런 상황에 인프런 홈페이지에서 워밍업 클럽 프로그램을 진행한다는 글을 보고, 일단 신청해서 해보자는 생각으로 프로그램을 시작했습니다. 강사님의 강의 안내처럼 백엔드의 모든 과정을 아우르며 진행하는 것이 이 강의의 목적이라고 했고, 그 말처럼 강의를 따라가다 보면 작은 기능이 하나씩 완성되고 있습니다. 재미있는 점은 강사님이 말한 것처럼 백엔드 과정 중간중간에 이해가 되지 않았던 부분들이, 강의를 보며 혼자 공부할 때 이해하지 못한 과정들의 목적을 알게 되어 이해가 깊어지고 있다는 것입니다. API 만들기, DB, 테이블 만들고 연결하기, 스프링 컨테이너와 빈, 컨트롤러, 요청(Request), 서비스(Service) 목적에 맞춰 나누기 등등 많은 것을 배웠습니다. 재미있는 것은 강의를 들으면서 왜 이렇게 하지? 목적이 뭐지? 등 강의를 들을 때 생기던 질문들이 여지없이 다음 강의에서 설명되는 것을 보며, 강사님이 강의에 대해 많은 고민을 하셨다는 생각이 들었습니다. 미션에 대해 하고 싶은 말이 많습니다. 강의 내용과 연관 있는 내용을 조사하거나 코드로 구현하는 것이었습니다. 잘 이해했다고 생각한 내용들이었는데, 끝내는 데 생각보다 많은 시간을 쏟았습니다. 확실히 미션을 통해 혼자 공부할 시간을 가지니 강의 내용에 대한 이해도가 많이 올라가는 것을 느꼈습니다. 그리고 다른 사람들의 글을 보면서 제 부족한 점도 느꼈습니다. 가장 오래 걸린 문제는 4일 차 미션인 API 만들기였습니다. CRUD를 처음으로 직접 만들어보는 것이 이번 강의가 처음이었는데, 분명 이해했다고 생각하고 자신 있게 미션을 시작했지만, 요청(Request) 부분을 만드는 데 상당히 많은 시간을 사용했습니다. 최소 이틀은 온 정신이 4일 차 미션에 가 있었습니다. 이후 강의에서 컨트롤러, 요청, 서비스로 목적에 맞게 분리하는 방법을 배우고 적용해보면서, 혼자 공부할 때 배웠던 내용이 이번 강의와 겹치며 이해가 되는 경험이 신기했습니다.

DK 1일 전
이슬

인프런 워밍업 클럽 스터디 1기 FE 과제(4번, 5번, 6번 과제)

[4번 과제(Day5) - 책 리스트 나열 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 5 ~ 6https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/book-list-app과제 이미지input 전체에 값의 유무에 따라 submit 버튼 활성화를 설정해줘서 정확한 제출 요청이 되도록 작업했다.setTimeout으로 추가, 삭제 알림이 3초 뒤에 사라지게 했다.리스트 그룹에 이벤트리스너를 설정해줘서 이벤트타겟의 태그가 버튼이라면 삭제버튼을 가진 부모요소를 삭제하도록 이벤트 위임으로 작업했다.[5번 과제(Day5) - Github Finder 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 5 ~ 6https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/github-finder-app과제 이미지import { Octokit } from "https://esm.sh/@octokit/core";위 스크립트를 import를 작동시키기위해서 html에 작성한 script태그에 type을 module로 설정하였다.유저의 프로필 정보는 고정적이라 값만 제외하고 퍼블리싱했고, 레포지토리 리스트는 script에서 innerHTML으로 한번에 삽입했다. li 태그를 계속 create하는 것보다 효율적이라고 생각했다.!username && alert("유저 이름을 입력해주세요."); // 변경 전 if (!username) return alert("유저 이름을 입력해주세요."); // 변경 후둘 다 username가 비어있거나 존재하지않을 때에만 조건이 참이 된다.변경 전 usename이 빈 칸일 경우 알림창이 뜨도록 조건식을 작성했는데, 알림창을 한번 나타나게하면 조건식이 끝나도 함수를 이어서 실행시켰다.내가 원하는 건 조건이 참이면 함수를 중지하는 로직을 원하기때문에 return문이 들어갈 수 있도록 조건식을 조건문으로 변경하였다.식과 문을 잘 구별해서 쓰자.[6번 과제(Day6) - 비밀번호 생성 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 7 ~ 8https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/password-generator-app과제 이미지비밀번호를 생성하는 generator 객체를 활용해보았다.generator 안에 아래 작성된 최소, 최대 길이에 맞게 참이 될때만 실행하도록 while 조건문을 설정했다.navigator.clipboard로 복사하기 기능을 구현했다.generator를 이번 강의에서 처음 배우고 사용해봤는데 목적에 맞게 가독성이 좋아보여서 흥미로웠다. 하지만 generator로 구현하는 것과 일반 함수로 구현하는 것의 차이를 아직은 잘 모르겠다.

웹 개발인프런워밍업클럽FE1기과제