블로그

이용수

[인프런 워밍업 클럽 1기] BE 1일차 과제

[인프런 워밍업 클럽 1기] BE 1일차 과제 [질문]Q. 어노테이션을 사용하는 이유 (효과) 는 무엇일까?  A. 어노테이션을 사용하는 이유에 대해 아래 내용에서 어노테이션의 정의, 특징, 역할, 장,단점에 대해 자료를 찾아 정리해보면서 나름대로 다음과 같은 결과를 도출 했다.어노테이션을 사용하면 코드와 설정을 같은 위치에 배치하여 코드의 가독성을 향상시킨다.클래스, 메서드, 필드, 파라미터 등과 관련된 정보가 함께 있어 코드를 읽고 이해하기 쉬워지며 특히, 코드의 흐름을 파악하기 쉬워진다.별도의 설정 파일을 작성하지 않고도 어노테이션을 사용하여 설정을 간소화할 수 있다. 이는 개발자가 코드에 직접 설정을 기술할 수 있으므로 설정 관리를 단순화시킨다.어노테이션을 통해 공통적인 코드 패턴이나 설정을 재사용할 수 있다. 이는 코드의 중복을 줄이고 효율적으로 코드를 작성할 수 있도록 도와준다.필요한 기능이나 제약 사항을 정의하기 위해 커스텀 어노테이션을 직접 정의할 수 있다. 이것은 프로젝트에서 특정한 요구사항에 대응하기 위해 유연하고 효율적인 방식으로 사용할 수 있다.어노테이션 프로세서를 사용하면 컴파일 시점에 어노테이션을 처리하고 검증할 수 있다. 또한, 코드를 자동으로 생성하거나 수정할 수 있어서 프로젝트에 필요한 기능을 효과적으로 구현할 수 있다. 어노테이션의 정의자바에서 어노테이션(Annotation)이란 소스 코드에 메타데이터를 추가하는 방법을 제공하는 기능이다. 어노테이션의 특징어노테이션은 컴파일러나 런타임 환경에게 정보를 전달할 수 있으며 전달된 정보는 코드를 실행하거나 컴파일할 때 사용된다.어노테이션은 @ 기호로 시작하며, 주석과 유사하게 생겼지만, 주석과는 달리 컴파일러가 읽고 처리할 수 있다.정의된 어노테이션은 해당 타겟에 대한 동작을 수행하는 프로그램 외에는 다른 프로그램에게 영향을 주지 않는다.        어노테이션의 역할컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.런타임에 특정 기능을 실행하도록 정보를 제공한다.  어노테이션 사용의 장점코드의 가독성 향상어노테이션은 코드와 설정을 같은 위치에 배치하므로 읽고 이해하기 쉽다. 클래스, 메서드, 필드, 파라미터 등 연관된 코드와 가까이 있기 때문에 흐름을 따라가기 쉽다.설정의 간소화별도의 설정 파일 작성 없이 어노테이션 적용을 통해 설정을 간소화할 수 있다.중복 코드 제거공통적인 코드 패턴이나 설정을 재사용할 수 있기 때문에 코드의 중복을 줄이고 효율적으로 코드를 작성할 수 있다.커스텀 어노테이션 정의직접 커스텀 어노테이션을 정의함으로 필요한 기능이나 제약 사항을 정의하여 사용할 수 있다.프로세서를 통한 검증 및 코드 생성어노테이션 프로세서를 이용해 컴파일 시점에 어노테이션을 처리하고 검증할 수 있다. 또한 코드를 자동으로 생성하거나 수정할 수 있기에 효과적으로 기능을 구현할 수 있다. 어노테이션 사용의 단점런타임 오버헤드런타임 시점에 리플렉션을 사용하여 처리하는 어노테이션의 경우 성능상의 오버헤드가 발생할 수 있다.컴파일 시점 제한어노테이션도 컴파일 시점에 오류를 확인할 수 있지만, 어노테이션 로직이 런타임에 에러를 발생시키거나 어노테이션에 잘못된 값이 할당된 경우 컴파일 시점에 오류를 확인할 수 없을 수도 있다. 어노테이션의 종류어노테이션은 크게 세 가지로 구분된다. 자바에서 기본적으로 제공하는 빌트인 어노테이션과 어노테이션을 정의하는 데 사용되는 메타 어노테이션, 마지막으로 사용자 어노테이션이 있다. 빌트인 어노테이션자바에서 기본적으로 제공하는 어노테이션이다.@Override : 컴파일러에게 메서드를 오버라이딩하는 것이라고 알린다.@Deprecated : 앞으로 사용하지 않을 대상임을 알린다.@FunctionalInterface : 함수형 인터페이스라는 것을 알린다.@SuppressWarning : 컴파일러가 경고 메시지를 나타내지 않는다.@SafeVaragrs : 제네릭과 같은 가변 인자의 매개변수를 사용할 때의 경고를 나타내지 않는다. 메타 어노테이션어노테이션에 붙이는 어노테이션으로, 어노테이션을 정의하는 데 사용한다.@Target : 어노테이션을 정의할 때 적용 대상을 지정하는 데 사용한다.@Documented : 어노테이션 정보를 javadoc으로 작성된 문서에 포함시킨다.@Inherited : 어노테이션이 하위 클래스에 상속되도록 한다.@Retention : 어노테이션이 유지되는 기간을 정하기 위해 사용한다.@Repeatable : 어노테이션을 반복해서 적용할 수 있도록 한다. 사용자 정의 어노테이션사용자가 직접 정의하여 사용하는 어노테이션이다. Q. 나만의 어노테이션은 어떻게 만들 수 있을까? A. 사용자가 직접 여러 어노테이션을 혼합하거나 정의하여 어노테이션을 만들 수 있다.기본적으로 인터페이스를 정의하는 것과 유사하며 @interface 뒤에 사용할 어노테이션의 이름을 정의하고 속성을 설정한다.어노테이션을 정의할 때 기본적으로 포함되야할 메타 어노테이션이 존재한다.@Retention - 어노테이션이 유지되는 기간을 정해야 하며 다음과 같은 열거 상수 중 선택한다.SOURCE : 컴파일할 때 적용 ~ 컴파일된 후에 제거됨CLASS : 메모리로 로딩할 때 적용 ~ 메모리로 로딩된 후에 제거됨RUNTIME : 실행할 때 적용 ~ 계속 유지됨@Target - 어노테이션을 정의할 때 적용 대상을 지정해야 하며 다음과 같은 열거 상수 중 선택한다.TYPE : 클래스, 인터페이스 열거타입ANOTATION_TYPE : 어노테이션FIELD : 필드CONSTERUCTOR : 생성자METHOD : 메서드LOCAL_VARIABLE : 로컬 변수PACKAGE : 패키지 이제 예를 들어 @MyAnnotion이라는 String 속성을 가진 어노테이션을 정의해보겠다.import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 사용자 정의 어노테이션 선언 @Retention(RetentionPolicy.RUNTIME) // 어노테이션 정보를 유지할 시점 (런타임까지 유지) @Target(ElementType.METHOD) // 어노테이션이 적용될 대상 (메서드에 적용) public @interface MyAnnotation { String value() default "test"; // 어노테이션의 속성, 기본값은 빈 문자열 // 추가적인 속성이 필요하다면 여기에 선언할 수 있습니다. }정의한 어노테이션에 사용한 메타 어노테이션은 다음과 같다.@Retention(RetentionPolicy.RUNTIME): 어노테이션 정보를 런타임까지 유지한다는 것을 의미하며 이렇게 하면 실행 중에 리플렉션(reflection)을 사용하여 어노테이션 정보를 읽을 수 있다.@Target(ElementType.METHOD): 이 어노테이션은 메서드에만 적용하도록 설정한다. 이제 메인 코드에서 어노테이션을 사용하고 리플렉션을 통해 어노테이션의 정보를 출력해보자.public class Main { @MyAnnotation public void myMethod() { System.out.println("This is myMethod"); } public static void main(String[] args) throws Exception { Main example = new Main(); example.myMethod(); // 리플렉션을 이용하여 어노테이션 정보 출력 java.lang.reflect.Method method = Main.class.getMethod("myMethod"); MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); System.out.println("Method name: " + method.getName()); System.out.println("Annotation value: " + annotation.value()); } }어노테이션의 이름과 속성 값을 출력하는걸 확인할 수 있다."C:\Program Files\Java\jdk-11.0.16.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.1\lib\idea_rt.jar=6433:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\User\Desktop\test\out\production\test Main This is myMethod Method name: myMethod Annotation value: test Process finished with exit code 0참고 자료[Spring] 스프링을 어노테이션 기반으로 만든 이유 (tistory.com)[Java] Custom Annotation(커스텀 어노테이션) 만들기 - MangKyu's Diary (tistory.com) 

백엔드인프런워밍업백엔드

한국 IT 용어 이야기 (3) - "R&R"

여러 직군의 사람들과 일을 하게 되었을 때, 애자일/스프린트/회고/리뷰 등으로 이런 저런 일들을 진행하게 될 때 듣게 되었던 단어 중에 생소한 것으로 "R&R"이 있었다. 스쳐 지나듯 오갔던 말이어서 한글로 "알앤아ㄹ" 혹은 "아래날" 정도로 들려서 꽤 오랫동안 'arena' 를 이야기하는 줄 알고 있었다. 'arena' 는 발음으로는 '어리나'에 가깝지만, 한국에서는 '아레나' 라고 듣고 자라 왔었기에 한데 모여 전투적으로 열심히 일하자는 이야기겠구나 생각했었는데, 사실은 정반대의 의미를 가진 단어였던 셈이었다.참고로 구글, 바드, 웹스터 등에 R&R 을 물어보면 Rest & Recreation 을 알려 주는데https://www.merriam-webster.com/dictionary/R%20%26%20R여기에 한글로 '뜻'이라 물어 한국어를 섞어 주면 IT 용어들로 쏠려서 결과들이 몰려 온다. 관련업에 종사하긴 하지만, 한국어 컨텐츠가 쏠려 있는 거 같아 조금 씁슬해 진 부분도 있겠다.https://www.google.com/search?q=R%26R+%EB%9C%BB알앤알 뒤에 붙는 단어들로는 '정리하다', '구분하다'가 많이 오고, 비슷한 문맥에 '업무분장'이라는 이름의 단어도 종종 등장한다. 업무분장은 job assignment , task assignment 등에 더 가깝다 하겠다.몇몇 기억들주로 여럿이 모여 일을 같이 하면서 혹은 나누어 하게 될 때 '선을 긋는' 용도로 자주 쓰였던 기억이다. 팀간에 혹은 멤버들 간에 가벼운 텐션이 있게 될 경우 나는 여기까지만 할 거고 그쪽에서 나머지는 알아서 하라 정도의 거리 두기 용으로 ...프로젝트 단위 보다는 조직도 같이 큰 그림에서 이해하기에 괜찮은 개념들이긴 하지만, 뭔가 훨씬 더 규모가 큰 곳들 - 영업망 업권 할당 같은 - 에서 쓰이는 게 좋은 개념이 과하게 스타트업씬에 내려와 있는 게 아닐까 싶었다. 특히 'responsibility' 부분은 과제에 적용시키기 어렵다는 생각인데, 실제로 '책임을 진다'는 게 어떤 의미인가에 대해 딱 떨어지는 그림이 나오진 않았고, 이는 내가 각종 툴의 "assigned" 상태에 익숙해져 있는 bias 가 있다 하겠다.아이러니하게 구글에 다니면서는 한 번도 써 보지 않은 단어였고, 그래서인지 개인적으로 개발 조직 내에서는 적어도 선을 그으면 안 된다는 생각이다. 잘 풀릴때는 뭐 별 문제 없지만, 왠지 과제가 삐걱거릴 때 무의식적으로 선을 긋는 습관이 여기서 온 거 같다는 생각이고, 몇몇 경우 개인들과 조직의 성장을 막는 요소로 작용하고 있지 않았나 하는 생각이다. 예를 들면 '나는 프론트엔드 엔지니어니까 백앤드는 고치면 안돼' 같은..선을 긋고 기본 자세가 방어적인 데서 시작을 하는 팀들과 복잡한 일을 해 나갈 때, 자연스레 비는 부분에 대해 책임 소재가 불분명해져서 종종 어려운 일들이 생겼다. 사람 수가 일감의 수보다 부족한 거의 모든 스타트업 씬에서는 특히 자주 일어나는 일인데, 새로운 영역의 일이거나 몇몇 팀들의 사이에서 겹치거나 비거나 하는 경우 자발적으로 알아서 챙겨 지면 좋으련만.. 이걸 잘 나누어서 일이 되게 잘 시키는(?) 것도 PM 이나 리더십의 R&R 이라 생각할 수도 있겠다.

교양용어개발자한국어

한국 IT 용어 이야기 (2) - 정합성

두번째로 챌린징했던 단어는 '정합성'이다. PM / design 쪽에서 이야기는 많이 듣지 못했지만, data 직군과 DBA , DevOps 들과 이야기할 때 종종 나왔던 단어이다.일단 네이버 사전에서 정합성은 무슨 말인지 못 알아들을 정도의 설명인데, 단지 뒤에 '체크'라는 말이 붙으면서 조금 알아들을 수 있는 용어로 바뀌게 된다.네이버 사전 결과 '정합성'30년 전에 데이터베이스 과목을 수강한 후에 실무 일머리들은 영어로 다시 다 배웠기에 여러 가지 용어들을 두리뭉실하게 써 왔는데, 여기서 잠깐 ChatGPT 와 bard 의 이야기 먼저...ChatGPT 의 결과 - "데이터 정합성을 영어로"bard 의 결과 - "데이터 정합성을 영어로"일단 책에서 배운 개념으로 data consistency 와 data integrity 가 꼬이기 시작했고, 한글로 적당한 '데이터 무결성'이 생각이 났다. 이를 비교하려 다시 물어 보니 이제 bard 랑 chatGPT 가 비슷한 말을 하게 되는 거 같았다.ChatGPT - 데이터 무결성과 데이터 정합성 비교bard - 데이터 무결성과 데이터 정합성 비교아래는 배웠던 대로 이해하고 동작하는 (쉬운) 예제들.database migration 작업을 하는데, 새로 생성된 테이블의 entry 개수가 이전 table 의 개수와 다르다.. --> 두 테이블의 정합성이 맞지 않아 AWS DMS 를 다시 시도한다든지...database , table 안에 끊어진 reference 들이 있고, deprecated 된 table 때문에 의미 없는 필드들이 더 생기게 되었다. --> data 무결성이 깨지는 상황으로 batch 잡을 돌려서 null 로 채우자.. 조금 난이도가 있는 사례로는소스로 삼는 raw table 이 여러 곳에서 동시에 사용되는 derived table 을 만들게 되는데, 같은 날 생성된 다른 두 테이블의 같아야 할 값이 다르더라. --> 두 테이블 사이에 필드들이 정합성이 다르다. freezing 되어 있는 테이블을 써라.. 그런데, 위의 bard 의 번역처럼 다양한 의미를 두리뭉실하게 '정합성'이라는 말에 기대어 쓰는 경우들이 종종 있었다. 뭔가 딱히 깊이 설명하고 싶지 않지만, 보이는 데이터를 바로 쓰기 찜찜할 때 '정합성' 이 거론되었고, 사실 이 단어 뒤에 들어오게 될 동사를 고르는 것도 꽤 어려운 일이다. '맞지 않다' , '깨져 있다', '좋다 or 나쁘다'. '완벽하다', '쓸만하다?'가장 어려웠던 사례로는Google Analytics 가 주는 MAU, Firebase 가 주는 MAU , Amplitude 가 주는 MAU 가 다른데, 데이터 정합성이 의심되니 쓰던 걸 쓰도록 하겠다. or vice versa실험을 돌려 지표가 나왔는데, 정합성에 이슈가 있어서 다시 하기로 했다. 이 '정합성'이라는 말은 '무결성'에 비해 조금 과하게 넓게 쓰이고 있는 게 아닐까 하는 생각이었고, 이 일본식 한자들은 딱히 정이 가지 않기도 해서 어느 새 지나 보니 시간 될 때마다 영어 표기를 권하는 꼰대가 되어 있었다.

교양용어개발자한국어

mikro-orm 버그 리포팅 후기 (feat. auto increment)

이슈등록 링크: https://github.com/mikro-orm/mikro-orm/issues/5460 [ 문제 상황 ] 사내에서 mikro-orm 을 Mysql 과 사용하고 있는데, 사내 db는  짝수 채번을 하고 있는데, 이상하게 mikro-orm 에서 여러 entity 를 영속화하면 pk 가 순차적으로 나오는 문제가 있었다.  [ 이슈의 정체 ] JPA의 경우 ORM 채번을 할 때, 아래의 mysql ok packet 을 통해서 이루어진다. https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html 이후에 auto_increment 에 대한 설정 값을 요청하여서, 이를 기반으로 영속성 컨텍스트에 auto_increment pk 를 세팅을 하기 마련이다. 단순 repository.save 호출을 한다고 가정하면, 아래의 패킷이 날아간다. 1. begin 2. insert into 'table' ('데이터')3. commit 그리고 2번 요청의 응답 패킷(OK packet)에 자세히 보면 아래와 같이 Last INSERT ID 라는 값이 내려온다. 이 값을 기준으로 entity 에 id 를 세팅해준다. (참고로, commit 되기전에 db 에서는 채번이 되고, db 에서 한번 채번되는 경우에 tx 가 롤백이 되더라도 이후에 같은 값을 사용하지는 않는다 -> pk 는 항상 순차가 아닐 수도 있음) 사실 이런 부분은 mikro-orm 도 똑같을거라고 생각을 했는데, mikro-orm 자체가 어떤 특별한 재주를 부리는건 아닐거라서 MySQL 서버와의 통신을 통해서 채번한 결과를 사용하는건 당연해보였다.  그런데, 아래와 같이 여러 개의 entity 를 저장하는 경우에 이상하게 pk 가 순차로 나왔다. (실제 코드는 아니고, 단순화하였다)mysql 채번 결과를 바탕으로 entity 의 pk가 정해질 텐데,  pk가 순차적으로 생성되니 의아했다.const result = await this.postRepository.save([ new PostEntity('title'), new PostEntity('title2'), new PostEntity('title3'), ]); return result; } @Injectable() export class PostRepository { constructor( @InjectRepository(PostEntity) private readonly postRepository: EntityRepository<PostEntity>, ) {} async save(entity: PostEntity | PostEntity[]): Promise<PostEntity | PostEntity[]> { await this.postRepository.getEntityManager().persistAndFlush(entity); return entity; } }  그래서, 해당 패킷이 어떻게 나가는지 wireshark 를 통해 패킷 분석을 해보았다 wireshark 를 사용하여 packet 을 확인해보니 auto_increment 에 대한 쿼리가 패킷으로 잘 날아갔다.  Ok packet이 잘 왔고, auto_increment 변수에 대한 값도 잘 조회하고 있었다.  그런데 결과는 아래와 같았다. - entity: pk 는 1씩 증가- tx commit 이후 db: db 레코드의 pk는 2씩 증가하고 있었다. mikro-orm 버그가 확실해 보였고, 브레이크 포인트를 찍어서 확인을 해보았다.  아래 코드에서 this.autoIncrementIncrement 필드에 1을 세팅하고 있는게 문제의 원인이었다.mikro-orm 의 connection 은 mikro-orm/knex 라는 라이브러리를 통해 이루어지는데, 여기서 res는  Value 로 내려오는데,할당 시에는 res?.auto_increment_increment 로 할당하고 있던게 문제였다.async getAutoIncrementIncrement(ctx) { if (this.autoIncrementIncrement == null) { // the increment step may differ when running a cluster, see https://github.com/mikro-orm/mikro-orm/issues/3828 const res = await this.connection.execute(`show variables like 'auto_increment_increment'`, [], 'get', ctx, { enabled: false }); /* istanbul ignore next */ this.autoIncrementIncrement = res?.auto_increment_increment ? +res?.auto_increment_increment : 1; } return this.autoIncrementIncrement; }  [ 후기 ] 사실 팀 내에서 이 문제에 대해서 인지는 하고 있었고, 다른 방식으로 문제를 해결하고 있었다. 그런데 시간이 나서, 트러블 슈팅을 해보았고 위와 같은 이슈가 있다는 것을 알게 되었던 것이다.  그래서 이 부분에 대해서 이슈를 등록했고, 올라온지 몇시간도 안되어서 수정되었다. 사실 처음부터 pr 을 올릴까 말까 고민을 했는데, orm 특성상 driver 의 버전에 의해 생기는 버그일 수도 있다고 생각을 해서 pr 을 올리지 않고 상세하게 이슈 리포팅을 했다. 무지성 pr을 올릴걸하는 아쉬움이 남는다 ㅋㅋtistory 게시글 링크: https://pius712.tistory.com/22

백엔드mikro-rommikroormnodejsnestjs

김종한

[찍먹클럽] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈 강의 후기 feat.인프

[찍먹클럽] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈 강의 후기 feat.인프런 인프런_찍먹클럽 Unity 개발 교육이번 KG 카이로스 교육에서 Unity를 사용해 프로젝트의 질을 높이기 위해서 많은 강의를 찾던 중..."인프런" 에서 Unity 강의를 1달간 무료로 제공되는 [찍먹클럽] 이벤트를 모집했었습니다!아침 Unity를 입문에 관련 교육이 필요했었고 특히나 돈이 없는 취업 준비생 입장에서 무료 강의는정말 달콤한 소식이 아니었나 싶었습니다.찍먹클럽에 선정되었다!내가 정말 열심히 블로그를 운영해서 그런가?운이 좋게도 [찍먹클럽] 에 선정되었다![C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진출처 : 인프런https://bit.ly/3V7xZts이번에 무료로 들었던 강의는 위의 링크를 통해서 자세하게 확인할 수 있습니다!무엇보다도 인프런은 높은 질의 강의를 제공하는 플랫폼으로 잘 알고 있었습니다.특히나 개발자들도 많이 찾는 강의 플랫폼이어서 더욱 신뢰가 갔었습니다.거기다가... 강사님의 엄청난 이력까지... 열심히 들을 이유가 생겼습니다!우선 강의에 대해서 좋은 점을 말씀드린다면1) 부드러운 진행과 필요한 내용으로 구성된 효율적 강의2) Unity 입문하면서 느끼는 Assets, Hierarchy처럼 복잡한 내용들을 집어줌3) 모델링만 하는 것이 아는 C#에 대한 내용도 포함된 복합적인 강의4) 개발하기 좋은 환경 구성까지 잡아 줌 (*레이아웃 설정, 폴더 정리 등)출처 입력가장 좋은 점이라면 [몰입도] 있는 강의가 아니었나 싶었습니다.아직 강의를 전부다 듣지 못했기 때문에... 지금까지 강의를 보고 구현한 정도만 리뷰하겠습니다! <Notion을 통한 강의 내용 정리>강의에서 중요한 내용들은 Notion에 따로 정리 <C# 코드 구현고 동작은 Unity 씬에서 확인>Unity는 C# Script를 작성해 동작을 제어하거나 새로운 Assets들을 추가할 수도 있습니다.필요하고 주로 사용하는 코드들은 따로 정리해서 내가 개발하고 싶거나 Object를 동작할 때 자주 활용했습니다.이번 강의에 대해 좋은 점도 코드 리뷰도 같이 해주셔서 상당히 만족했습니다.Vector3에 대한 코드 리뷰와 동작 확인 <Prefab을 통해 나만의 Tank 만들어 보기>Assets에 Prefab을 만들어서 내가 원하는 Object들을 생성하고 저장합니다.각 Prefab은 Speed나 각도를 독립적으로 지정해 동작을 시킬 수 있습니다.허접해 보이지만 탱크다.나만의 Tank를 동작해 봄!나의 Unity 공부는 -ing출처 입력최근에 KG 카이로스에 프로젝트를 기획하고 팀원들과 의견을 나누는 과정에 있어서바쁜(*핑계 아님) 와중에 강의를 절반 정도 들었습니다!KG_KAIROS 최종 프로젝트 <안>그래서... 앞으로의 남은 강의를 전부 수강하게 된다면 어떤 Unity 환경 속에 내가 구성한 Object를동작하고 플레이할 수 있을지 기대가 됩니다!남은 강의... 그중 미니 RPG가 상당히 기대된다! 인프런 담당자님... 반드시 전부 수강하겠습니다. 믿어 주십쇼!미련한 수강생

그래픽 디자인인프런인프런강의후기게임개발게임개발강의인강후기강의후기UnityC#유니티엔진MMORPG

[인프런 찍먹클럽] 언리얼엔진5 스파르타 클래스 - 심화편 후기

언리얼 엔진5를 공부를 하다보면, 영어 강의들을 보게 된다. 수많은 영어 강의들한국어 강의를 찾던 중, 인프런의 언리얼엔진5 스파르타 클래스 - 심화편을 알게 되었다.심화편이라.. ㄷㄷ 벌써부터 궁금해지지 않나?! 강사님은 YAL 선생님!실전, 심화로 나누어 2개의 강의를 제작하셨다. 실전편은 무료이니 모든 분들이 체험할 수 있다. 언리얼엔진5 스파르타 클래스 - 심화편 : https://bit.ly/3T7YBbd 특히, 나는 애니메이션을 더욱 알고 싶어서 이번에 참가하게 되었다.언리얼 엔진의 애니메이션의 다양한 방식들을 알 수 있다. IK,FK를 보면 무슨 생각이 들까요? 피하고 싶어진다...😥하지만, 강의를 들으면서 애니메이션에 사용되는 용어들을 쉽게 알 수 있었다. IK: Inverse Kinematic 축을 활용해 bone을 움직임FK: 관절 인형 처럼 관절들을 조종해서 움직임을 구현출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅  리타게팅! 애니메이션을 다른 캐릭터에 복사를 하는 것이다. 당연히 그대로 사용하면 bone의 사용방식이 다를테니 망가진다. 동기화를 해주기 위해서 사용하게된다. 1. 먼저 리타겟팅을 위해 bone 체인을 설정해준다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅2. 리타겟팅 에셋에서 Source IK Rig와 Target Ik Rig를 비교하면서 오차들을 수정하며 동기화 시킨다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 3. 리타겟한 결과를 볼 수 있다.https://youtu.be/b9l-7svKbP4출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 이 방식들을 강의를 들으면서 따라하면 쉽게 제작이 가능하다.중요한 사실은 이 강의를 기반으로 자신의 애니메이션을 제작하는데 활용 할 수 있다. 물론, 강의를 따라해도 안되는 경우들이 생긴다. 나도 물론 생겨서 당황스러웠지만, 커뮤니티 게시판으로 해결을 하였다.언리얼 엔진5 스파르타 클래스 - 심화편 커뮤니티 게시판여기 게시판을 활용해서 선생님께 직접 질문을 하고 답변을 받을 수 있으니, 걱정 안해도 될 것이다. 후기나는 이번 찍먹클럽을 통해서 애니메이션에 대한 두려움이 사라지게 되었다. 배운 것들을 기반으로 나의 프로젝트에서 애니메이션을  활용할 수 있음을 확인하여 나에게 만족스러운 강의가 되었다. 이 강의를 자신이 들어도 괜찮을까 고민 하는 분들!난이도가 매우 쉽게 구성되어 있고 커리큘럼이 단계적으로 잘 구성되어 있다. 선생님과 함께 따라서 제작한다면 무리가 없을 것이다.언리얼 엔진5 에디터도 무려 한국어로 되어 있어, 영어가 어려워 접근하기 쉽지 않은 분들도 할 수 있는 쉬운 접근 중 하나가 될 것이다. 모두 스파르타 클래스 - 심화편을 듣고 언리얼 엔진5를 마스터 할 수 있도록 노력하자! 화이팅!

게임 프로그래밍인프런인프런강의후기게임개발게임개발강의인강후기강의후기게임개발자인프런강의

sql - section 1

관계형 DBOLTP (Online Transaction Processing): 트랜잭션을 하기위한 데이터베이스특징보류, 중간 상태가 없어서 데이터의 무결성을 유지할 수 있다.데이터 추가, 변경이 많다.쿼리 속도가 느리다.트랜잭션: 데이터베이스의 상태를 변화시키기 위해 수행되는 작업의 단위관련 DBMSoracle데이터 베이스 시장 1위높은 안정성과 유지보수 보장비싼 가격mysql오픈소스데이터 베이스 시장 2위postgreSQL오픈소스mysql보다 sql 표준을 잘 지원하며, 쿼리가 복잡해질 수록 성능이 더 잘 나옴mssql대규모 엔터프라이즈 수준의 시스템에 적합주로 윈도우 환경에 사용default isolation level이 read committed데이터를 읽을 때 공유잠금이 유지 -> WITH(NOLOCK)을 통해 공유잠금없이 데이터 조회OLAP(Online Analytical Processing): 데이터 웨어하우스를 이용해, 분석질의를 처리 목적으로 만들어진 데이터베이스쿼리 속도가 빠른 편데이터 웨어하우스: 분석가능한 정보의 중앙 라포지토리관련 DBMS빅쿼리구글 클라우드의 OLAP + data warehouse 사용컴퓨팅 레이어와 스토리지 레이어 분리각 레이어가 다른 레이어에 영향을 안 미침비관계형 DB (NOSQL)특징key - value 형식을 지원PK,FK JOIN을 지원하지 않음스키마에 대한 정의가 없음장점대용량 데이터 처리에 유리관계형 데이터베이스보다 읽기, 쓰기 속도가 빠름데이터 모델링에 유리분산처리에 유리데이터의 일관성을 보장하지 않아도 되고, join 연산이 필요없을 때 사용하면 유리관련 DBMS몽고DBRedis  

데이터베이스

코다

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

3주차 발자국📍2024.05.13 ~ 2924.05.16🏁 3주차 미션😎 강의 수료강의 요약#6 React TDD 기본TDD (Test Driven Development) 란 테스트 코드를 먼저 작성하고, 테스트 코드를 Pass 할 수 있는 실제 코드를 작성하는 개발 방법이다. 안정적인 소스 코드, 디버깅 시간 및 개발 시간 감소, 깨끗한 코드의 장점이 있다.React Testing Library리액트 컴포넌트에 대한 테스트를 사용자 중심으로 작성하는데 도움을 주며 사용자의 행동에 대한 테스트를 강조하는 라이브러리이다. 리액트 구성 요소 작업을 위한 API 를 추가하여 DOM Testing Library 위에 구축된다. DOM Testing Library 는 DOM 노드를 테스트하기 위한 매우 가벼운 솔루션이다.render 함수 : DOM 에 컴포넌트를 렌더링 하는 함수이며 인자로 렌더링할 리액트 컴포넌트를 사용한다.쿼리 함수 : 페이지에서 요소를 찾기 위해 사용한다. get, find, query 유형이 있다.getBy : 쿼리에 대해 일치하는 노드를 반환하고 일치하는 요소가 없거나 둘 이상 일치하면 오류를 발생한다queryBy : 쿼리에 대해 일치하는 노드를 반환하고 일치하는 요소가 없으면 null 을 반환한다.findBy : 쿼리와 일치하는 요소가 발견되면 Promise 를 반환한다. 요소가 발견되지 않거나 기본 제한 시간 후에 둘 이상의 요소가 발견되면 프로미스가 거부된다. ( getBy + waitFor = findBy )waitFor : 일정 기간 동안 기다려야 할 때 사용하여 기대가 통과될 때까지 기다릴 수 있다.JestJest 는 Facebook 에 의해 만들어진 테스팅 프레임워크이다. 최소한의 설정으로 동작하며 테스트 케이스로 어플리케이션 코드를 확인한다. 단위테스트를 위해 이용한다.describe : 테스트를 그룹화하는 블록을 만든다it : 개별 테스트를 수행하며 각 테스트를 작은 문장처럼 설명한다.expect : 값을 테스트할 때마다 사용되며 matcher 와 함께 사용된다.matcher : 다른 방법으로 값을 테스트 하도록 할 때 사용된다.코드 형식 라이브러리Prettier : 코드 형식을 맞추는데 사용하며 코드 포맷터 역할을 한다.ESLint : 개발자들이 특정 규칙을 가지고 코드를 짤 수 있게 도와주는 라이브러리이며 코드 포맷터이기도 하지만 문법 오류를 파악하는 것이 주요 기능이다.#7 React TDD 를 이용한 간단한 앱 생성 및 배포FireEvent API : 유저가 발생시키는 이벤트에 대한 테스트를 하는 경우 사용한다.test('테스트 설명', () => { render(<App />); // 테스트를 위해 App 렌더링 const buttonElement = screen.getByTestId('plus-button'); fireEvent.click(buttonElemtn); // buttonElement 에 클릭 이벤트 발생 const counterElement = screen.getByTestId('counter'); expect(counterElement).toHaveTextContent(1); // counterElement 의 텍스트가 1 });Github Action 을 이용한 AWS S3 로 앱 자동 배포Jenkins, Circle CI, Travis CI, Github 등이 배포에 쓰인다. 강의에서는 Github 을 사용한다.순서저장소 생성 - 프로젝트와 연결 - Github 페이지에서 workflow (Node.js) 생성 - S3 버킷 생성 - 버킷 설정 변경 (정적 웹 사이트 호스팅 활성화) - 버킷 정책 변경 - Github workflow yml 파일에 앱 자동 배포 코드 추가 - IAM 사용자 추가 - Github 저장소 env 설정에 IAM 키 추가#8 Next.js 와 TypescriptNext.js리액트의 SSR (Server Side Rendering) 을 쉽게 구현할 수 있게 돕는 프레임워크이다.CSR 은 검색엔진에 최적화되어있지 않기 때문에 SSR 을 사용한다.Next.js 는 모든 페이지를 pre-render 하여 HTML 을 클라이언트에서 자바스크립트로 처리하기 전에 생성함으로써 SEO 검색엔진 최적화가 좋아진다.Next.js 에서 데이터를 가져오는 방법getStaticProps : Static Generation 으로 빌드할 때 데이터를 불러온다.페이지 렌더링 시 사용자 요청보다 먼저 필요한 데이터를 가져올 수 있을 때, 데이터를 headless CMS 에서 가져올 때, 모든 사용자에게 같은 데이터를 보여줄 때, 페이지가 미리 렌더링 되어야 하고 빨라야 할 때 사용한다.getStaticPaths : Static Generation 으로 데이터에 기반하여 pre-render 시에 특정한 동적 라우팅을 구현한다.path 는 어떤 경로가 pre-render 될 지 결정한다. params 는 페이지 이름이 pages/posts/[postId]/[commentId] 인 경우 postId, commentId 이다. fallback 이 false 이면 getStaticPaths 로 리턴되지 않는 것은 모두 404 페이지로 이동되며 true 인 경우는 fallback 페이지를 보여준다.getServerSideProps : SSR 로 요청이 있을 때 데이터를 불러온다.async 로 export 하면 Next.js 는 각 요청마다 리턴되는 데이터를 getServerSideProps 로 pre-render 한다. 따라서 다른 페이지로 이동했다가 이전 페이지로 돌아가도 최신 데이터가 반영된다. 요청할 때 데이터를 가져와야 하는 페이지를 미리 렌더링 할 때 사용한다.Static Site Generation (SSG) : 빌드 때 HTML 을 각 페이지별로 서버에 생성해 놓고 요청 시에 HTML 을 반환한다.TypeScript자바스크립트에 타입을 부여한 코드를 단순화하고 쉽게 디버그 할 수 있으며 타입 검사 및 컴파일 오류 검사의 기능을 수행하는 언어이다. 타입스크립트는 자바스크립트와 달리 브라우저 실행 시 파일을 한 번 변환하는 컴파일 과정을 거친다.타입스크립트의 타입은 자바스크립트의 타입들과 추가로 tuple, Enum, Any, Void, Never, Union 타입이 있다.any : 잘 알지 못하는 타입을 표현하며, 컴파일 때 타입 검사를 거치지 않는다. noImplicitAny 라는 옵션을 사용하면 any 타입을 썼을 때 오류를 발생시킬 수 있다.union : 둘 이상의 데이터 유형을 사용할 때 유니온 타입이라고 한다. ex. let code : (string | number);tuple : 지정한 순서와 타입만을 사용할 수 있는 특수한 형태의 배열 타입이다.enum : 열거형을 의미하며 별도의 값을 설정하지 않으면 0부터 시작한다.void : 데이터가 없고 타입이 없는 상태이다. any 와 반대의 이미이다.never : 절대 일어나지 않을 것이라고 확신할 때 사용하며 주로 함수의 리턴 타입으로 사용된다. 항상 오류를 리턴하거나 리턴 값을 절대로 내보내지 않음을 의미한다.void vs never : void 는 값으로 undefined, null 을 가질 수 있으나 never 는 어떠한 값도 가질 수 없다.type annotation : 개발자가 타입을 타입스크립트에게 직접 전달한다.type inference : 타입스크립트가 알아서 타입을 추론한다.type assertion : 값의 타입을 설정하고 컴파일러에게 유추하지 않도록 지시한다.#9 React Version 18Automatic batchingbatching 은 업데이트 대상이 되는 상태값들을 하나의 그룹으로 묶어 한 번의 리렌더링으로 모든 업데이트가 진행될 수 있게 하는 것을 말한다.적은 리렌더링, 이벤트 핸들러 밖에서도 작동하며 필요할 때 제외 가능한 것이 특징이다.Suspense on the server<Suspense /> 를 사용하여 앱을 더 작은 독립 단위로 나누어 앱 사용자가 콘텐츠를 빨리 보고 빠르게 상호작용할 수 있다.Streaming HTML : 모든 데이터를 가져오기 전에 renderToString 대신 renderToPipeableStream 을 사용하여 HTML 을 스트리밍하는 방법이다.Selective Hydration : 모든 코드가 로드되기 전에 hydrate 하는 방법이다.hydration : 자바스크립트 논리를 전체 앱에 대해 서버 생성된 HTML 에 연결하는 것이다.Transition업데이트에 urgent 를 주어 상태 업데이트 시에 우선순위를 주게 하는 기능이다.#10 Redux자바스크립트 애플리케이션을 위한 상태 관리 라이브러리이다. Redux 는 State 를 관리한다.Action -> Reducer -> Redux Store -> React Component -> (이벤트가 발생하면) -> ActionProvider : Redux Store 에 접근해야 하는 모든 중첩 구성 요소에서 Redux Store 를 사용할 수 있도록 한다.useSelector, useDispatch 를 이용해 provider 로 둘러싸인 컴포넌트에서 store 에 접근 가능하다.리덕스 미들웨어: 액션을 전달하고 리듀서에 도달하는 순간 사전에 지정된 작업을 실행할 수 있게 하는 중간자Redux Thunk : 리덕스를 사용하며 비동기 작업을 할 때 많이 사용한다.Redux Toolkit : 리덕스 로직을 작성하기 위한 공식 권장 접근 방식이다.Redux Persist : 페이지 새로고침 후에도 Redux Store 의 State 들을 유지할 수 있다.#11 도커를 이용한 리액트 실행Docker컨테이너를 사용하여 응용 프로그램을 쉽게 만들고 배포, 실행할 수 있는 컨테이너 기반의 오픈소스 가상화 플랫폼이다. 프로그램 다운로드 과정을 간단하게 만들기 위해 사용한다.컨테이너 안에 다양한 프로그램, 실행환경을 추상화하고 동일한 인터페이스를 제공해 프로그램의 배포 및 관리를 단순하게 하고 어디서든 실행 가능하게 한다.도커 클라이언트에서 커맨드 실행 - 도커 서버에서 이미지가 로컬에 캐시 되어있는지 확인 - 캐시된 이미지가 없는 경우 도커 허브에서 이미지를 가져와 로컬 캐시에 저장한다 - 이미지로 컨테이너를 생성한다 - 이미지로 생성된 컨테이너는 이미지에서 받은 설정이나 조건에 따라 프로그램을 실행한다.도커 파일, 이미지도커 파일 생성 순서 : 베이스 이미지 명시 - 추가로 필요한 파일 다운을 위한 명령어 명시 - 컨테이너 시작 시 실행될 명령어 명시베이스 이미지는 이미지의 기반으로 간단하게 OS 라고 생각하면 된다.이미지는 응용 프로그램 실행 시 필요한 모든 것 (실행 명령어, 파일 스냅샷)을 포함하고 있다.파일 스냅샷은 디렉토리나 파일을 카피한 것이다.도커 이미지 생성 순서 : 도커 파일 작성 - 도커 빌드로 도커 파일의 명령어들이 도커 클라이언트에 전달 - 도커 서버 - 이미지 생성포트 매핑이미지를 만들 때 로컬의 파일을 컨테이너에 복사하듯이 네트워크도 로컬 네트워크와 컨테이너 내부의 네트워크를 연결해줘야 한다. -p 키워드로 매핑을 할 수 있다. ex docker run -p 3000 : 3000 이미지 이름 -> 3000번 포트끼리 연결함도커 컴포즈 파일을 작성하면 도커 실행 시에 긴 명령어를 간단하게 만들 수 있다. 느낀점TDD 와 배포, 타입스크립트, 도커까지 배웠다. 테스트 코드를 직접 작성해보니 신기했다. 테스트를 용이하게 하기 위해 함수와 컴포넌트를 작게 만들어야 한다는 것을 확실히 깨달았다. 범위가 크면 테스트 코드 작성하기가 매우 복잡할 것 같다. 이전에 배포는 해본 적이 없어서 S3 에 코드 배포하는 것이 가장 흥미로웠다. 기존에 자바스크립트로 짰던 코드를 리팩토링 해볼 예정이다. 수업을 들을 때는 간단했는데 직접 하려고 했더니 많이 헷갈렸다. 시간이 부족해서 과제를 원하는 만큼 구현하지 못한 것 같아 아쉽다. 워밍업 클럽을 수료하고 나서도 남은 과제를 풀어야겠다.

프론트엔드인프런워밍업클럽

슬프구나

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

FE 강의 진도율 100% 달성자바스크립트 과제 완료음식 메뉴 앱가위 바위 보 앱퀴즈 앱책 리스트 나열 앱Github Finder 앱비밀번호 생성 앱타이핑 테스트 앱리액트 과제 완료예산 계산기 앱디즈니 플러스 앱포켓몬 도감 앱리덕스를 이용한 쇼핑몰 앱(Nextjs 사용) 드디어, 3주가 다 지나갔다.강의는 라디오를 청취하듯이 듣고, 과제를 진행하면서 막히는 경우에 재청취 하였다.자바스크립트 강의는 총 12시간, 리액트 강의는 총 18시간 분량이었는데 청취하면서 사실 쉽지는 않았다. 아는 내용은 가볍게 넘기기도 하였다. 개인적으로 좋았던 점은 강의에 대부분의 내용이 담겨있어서 복습하기 좋았다. 그리고 리액트 강의에서는 React v18 에 추가된 부분도 있고 Docker 를 사용하여 컨테이너 기반으로 리액트 프로젝트를 해보는 강의도 있어서 좋았다.React v18 에 제일 큰 변화는 향상된 automation batching 과 Suspense 가 SSR도 지원하여 Streaming HTML 이 아닐까 생각한다. SSR은 데이터가 다 준비가 되고 렌더링이 될 때까지 기다려야 한다. 하지만 리액트팀은 이러한 부분을 개선하여 UX를 향상 시켰다. 해당 스터디를 참여를 해야하나 조금 고민을 했었다. 혼자서 공부가 가능한 내용들이기 때문이었는데 결론적으로는 참여하길 잘 했다고 생각한다. 반 강제적이긴 하지만 과제들을 하면서 복기를 할 수 있었고 강의를 들으면서 내용을 다시 한번 흩어볼 수 있어서 유익한 시간이었다. 리액트 과제같은 경우에는 전부 TS 를 기반으로 작성 하였다. JS를 활용한 프론트엔드 또는 백엔드 개발에서 TS는 사실 이제 필수라고 생각한다. JS 자료형이 외부 도구에 의존을 해야 할 만큼 나쁜가? 라고 생각을 한다면 꼭 나쁘다고 단정 지을 수는 없다. 아무래도 동적 타이핑에서 발생하는 문제를 해결해주는 가치가 크기 때문이 아닐까 생각한다.동적 타이핑은 값이 할당 된 순간 타입이 결정이 되므로, 타입 관련 에러가 런타임 환경에 발생할 수 있다.동적 타이핑은 값이 할당 된 순간 타입이 결정이 되므로, 타입 관련 에러가 발생 했을 때 규모가 큰 프로젝트에서 이를 추적하기 어렵거나 예측하기 어렵다.위 문제를 해결하고자 우리는 TS 를 사용한다. 조기에 더 많은 오류를 포착해주기 때문이다. 그리고 정적 타이핑을 통한 코드 가독성이 향상 된다.(어떤 함수를 호출한다고 가정하면, 해당 인자가 어떤 타입인지 추론이 가능하기 때문이다)TS 를 사용함으로써 제일 큰 이점은 리팩토링이다. 예를 들어, API Response 명세가 바뀌었다고 가정하자. 프론트엔드단에서 이 명세에 맞게 모델을 바꿔야 할 거다. 해당 모델에 대한 타입이 정의가 되어있으므로 우리는 자신있게 이 부분을 리팩토링 할 수 있다.(틀리면 바로 에러로 알려주기 때문이다.)만약, 그냥 JS 환경이라면 실행이 잘 될수도 있고 놓친 부분이 있다면 런타임에서 에러가 발견이 될 거다. Angular 는 애초에 First party 로 TS 를 사용해 왔다.Vue, React 는 이후에 지원을 하기 시작했다. 이런거 보면, Angular 를 관리하는 구글이 선견지명이 있는거같다. 그렇지만, TS도 만능은 아니다.TSC 를 통해 나온 컴파일 결과물인 js 파일들을 확인해보면, TS 관련 코드들은 전부 사라져 있다. 결국에는 완벽하게 런타임 오류를 완전히 방지하지는 못한다. 그리고 TS를 사용하면 코드량이 증가하며 학습 비용이 발생하므로 이 부분도 프로젝트 규모의 따라 고민을 해보면 좋을 것 같다.(킹치만, 사용 안하면 너무 불안한걸?)

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

강호연

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

1. 강의 관련지난주에 다 들음2. 미션 관련 각 프로젝트별 상세는 각 게시물에서 확인바람.미션 3 포켓몬 도감 앱Github :https://github.com/KimPra2989/inflearn-warming-up-missions/tree/main/React/%EB%AF%B8%EC%85%98%203%20%ED%8F%AC%EC%BC%93%EB%AA%AC%20%EB%8F%84%EA%B0%90%20%EC%95%B1 인게임 느낌의 포켓몬 도감으로 디자인수정함방향키로 위아래 가능밑에서 15마리 정도 될 때 추가 데이터 페칭 (무한 스크롤)차트로 스텟 상태 나타냄상세페이지의 반짝이 버튼 누르면 사진이 이로치로 바뀜 (영상 안 찍었네;;)데이터 한글화함미션 4 퀴즈 앱Githubhttps://github.com/KimPra2989/inflearn-warming-up-missions/tree/main/React/%EB%AF%B8%EC%85%98%204%20%ED%80%B4%EC%A6%88%20%EC%95%B1JS 3미션 퀴즈 앱을 리액트로 마이그래이션했다. 견본 영상의 제목이 'Next로 만드는 퀴즈앱'이어서 next로 만들어야하나 고민을 했지만 next의 이점인, ssr이나, SEO, 이미지최적화 등 여러요소들이 굳이 필요하지 않다고 생각해서 react로 진행했다. 대부분 이전에 만든 프로젝트에서 갖고온 공통 컴포넌트와 로직을 재활용한 파트여서 그다지 어려운 점은 없었다. 회고저번주에 강의도 다 들었겠다 프로젝트에 몰입할 수 있다는 생각과 함께 자신감 넘치는 막주가 시작되....ㄹ 줄 알았으나 금요일에 시작된 그룹 프로젝트에 압도적 주니어로 들어가게 되어 과제를 더 이상할 수가 없었다....과제 3개 채우기용으로 가장 쉬운 퀴즈앱을 고르고, 완성도를 높이려했던 포켓몬 도감도 흐지부지 끝나고야 말았다. 태초의 목표가 다양한 프로젝트에 도전하는 것이었는데 그 부분은 어느정도 해결이 된 것 같아서 성취감이 없지는 않다. 다만, 깊이 있게 뭘 만들어보질 못했던 것이 후회로 남는다. 아쉽지만 프로젝트 때문에 이 정도만 쓰고 간다.ps. 디스코드 보니 중간 부터 과제결과물이 안 올라오는 것 같던데 사실 다 탈주하고 몇명만 남아있는 개꿀잼 몰카인가?... 재미없으니 다들 돌아왔으면 좋겠다.

구르밍

인프런 워밍업 클럽 BE 1기 - 3주차 발자국

조금 더 객체지향적으로 개발할 수 없을까?이전에는 User를 따로 가져와 BookService에서 UserLoanHistory를 만들어 저장하였다.조금 더 객체지향적으로 바꾸면 UserLoanHistory를 User에서 가져와 바로 대출을 처리하자!@ManyToOne : 내가 다수이고 너가 한개대출기록은 여러개이고 그 대출을 소유하고 있는 사용자는 한명이다 → N : 1 관계//private long userId; @ManyToOne private User user;@OneToMany : 나는 한개 너가 다수@OneToMany privateList<UserLoanHistory> userLoanHistoryList = new ArrayList<>();여러개의 대출기록 N개이기 때문에 List로 표현이로써 User와 UserLoanHistory는 서로 연관관계가 되었다. 그 중에 주인은 누구인가?누가 관계의 주도권을 가지고 있는가?현재는 user_loan_history가 user_id를 DB 컬럼으로 가지고 있기 때문에 주인이다.연관관계의 주인이 아닌 쪽에 mappedBy 옵션을 달아 주어야 한다.@OneToMany(mappedBy = "user") privateList<UserLoanHistory> userLoanHistoryList = new ArrayList<>(); 1:1 관계한 사람은 한 개의 실거주 주소 만을 가지고 있다.person 테이블이 address 테이블의 id를 가질 수도 있고, address테이블이 person 테이블의 id를 가질 수도 있다.@Entity public class Person{ ... @OneToOne private Address address; }@Entity public class Address{ ... @OneToOne private Person person; }이렇게 테이블을 생성한다고 가정할때 Person이 address id를 가지고 있다.create table person ( id bigint auto_increment, name varchar(255), address_id bigint, primary key (id) );create table address ( id bigint auto_increment, city varchar(255), street varchar(255), primary key (id) );→ Person이 address 주인이다! 1:1 관계지만..따라서 mappedBy 작성@Entity public class Address{ ... @OneToOne(mappedBy = "address") private Person person; }연관관계의 주인 효과 → 객체가 연결되는 기준이 된다.!@Transactional public void savePerson() { Person person = personRepository.save(new Person()); Address address = addressRepository.save(new Address()); person.setAddress(address); // setter는 임시 }person이 address의 주인이기때문에 이렇게 코드를 작성하고 실행하면 DB에서 정상으로 테이블이 연결된다. 하지만 반대로 1:1관계이더라도 address.setPerson(person);  이렇게 작성할 경우 DB에 저장되지 않는다. DB는 연결되었지만 !!! 객체끼리는 연결되지 않았다. address.getPerson(); --> 트랜잭션이 끝나기전에 get하면 Null 반환@Transactional public void savePerson() { Person person = personRepository.save(new Person()); Address address = addressRepository.save(new Address()); person.setAddress(address); // setter는 임시 address.getPerson(); --> 트랜잭션이 끝나기전에 get하면 Null 반환 }해결책으로 setter 한번에 둘을 같이 이어주면 된다.public void setAddress(Address address) { this.address = address; this.address.setPerson(this); --> 둘을 같이 이어주자 }N : 1 관계이 관계에서 주인은 무조건 숫자가 많은쪽이 주인이다.  @OneToMany를 작성하지 않고, @ManyToOne 하나만 작성해도 된다.(단방향)  @JoinColumn연관관계의 주인이 활용할 수 있는 어노테이션.필드의 이름이나 null 여부, 유일성 여부, 업데이트 여부 등을 지정@JoinColumn(nullable = false) @ManyToOne private User user; N : M 관계 - @ManyToMany학생과 동아리 관계를 생각하자학생은 여러 동아리를 가입할 수 있고, 한 동아리엔 여러 학생이 있다.→ But, 구조가 복잡하고, 테이블이 직관적으로 매핑되지 않아 사용하지 않는 것을 추천cascade 옵션 cascade : 폭포처럼 흐르다.  : 한 객체가 저장되거나 삭제될 때, 그 변경이 폭포처럼 플러 연결되어 있는 객체도 함께 저장되거나 삭제되는 기능유저가 삭제될때 유저가 연결되어 있는 UserLoanHistory도 삭제하고 싶을때 사용하면 된다.@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) privateList<UserLoanHistory> userLoanHistoryList = new ArrayList<>(); orphanRemoval 옵션: 객체간의 관계가 끊어진 데이터를 자동으로 제거하는 옵션관계가 끊어진 데이터 = orphan(고아) removal (제거)한 유저가 빌린 책1, 책2가 있다고 가정할 때, userLoanHistory에서 책1만 리스트(자바단)에서 지웠다. → DB는 아무런 변화가 없다. 이렇게 리스트에서 지우는(연결을 끊는 것 만으로도) 것으로도 DB에서 삭제가 되길바라면 이 옵션을 쓸 수 있다.@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) privateList<UserLoanHistory> userLoanHistoryList = new ArrayList<>(); @Transactional public void deleteUserHistory() { User user = userRepository.findByName("ABC") .orElseThrow(IllegalArgumentException::new); user.removeOneHistory(); } public void removeOneHistory() { userLoanHistories.removeIf(history -> "책1".equals(history.getBookName())); }  책 대출/반납 기능 리팩토링과 지연로딩User와 UserLoanHistory가 직접적으로 연결되어 있다.대출기능 리팩토링public void loanBook(String bookName) { this.userLoanHistories.add(new UserLoanHistory(this, bookName)); } User.java에서 새로운 UserLoanHistory객체를 만들고 넣어주기 때문에 service단에서는 loanBook메소드를 호출해주기만 하면 된다.@Transactional public void loanBook(BookLoanRequest request) throws IllegalAccessException { Book book = bookRespository.findByName(request.getBookName()) .orElseThrow(IllegalAccessException::new); if (userLoanHistoryRepository.existsByBookNameAndIsReturn(book.getName(), false)) { throw new IllegalAccessException("이미 대출중인 책입니다."); } User user = userRepository.findByName(request.getUserName()); if(user == null) { throw new IllegalAccessException("사용자를 찾을 수 없습니다."); } //userLoanHistoryRepository.save(new UserLoanHistory(user, book.getName())); user.loanBook(book.getName()); }반납기능 리팩토링public void returnBook(String bookName) throws IllegalAccessException { UserLoanHistory targetHistory = this.userLoanHistories.stream() .filter(history -> history.getBookName().equals(bookName)) .findFirst() .orElseThrow(IllegalAccessException::new); targetHistory.doReturn(); }함수형 프로그래밍을 할 수 있게 .stream을 작성해주고.filter를 통해 들어오는 객체들 중에 다음 조건을 충족하는 것만 필터링 한다  ..findFirst()를 통해 첫번째로 해당하는 UserLoanHistory를 찾는다.  이 결과는 Optional이기에 Optional을 제거하기 위해 없으면 예외를 던진다.  orElseThrow그렇게 찾은 UserLoanHistory를 반납처리 한다.@Transactional public void returnBook(BookReturnRequest request) throws IllegalAccessException { User user = userRepository.findByName(request.getUserName()); if(user == null) { throw new IllegalAccessException("사용자를 찾을 수 없습니다."); } // UserLoanHistory history = userLoanHistoryRepository.findByUserIdAndBookName(user.getId(), request.getBookName()) // .orElseThrow(IllegalAccessException::new); // history.doReturn(); user.returnBook(request.getBookName()); }userLoanHistoryRepository.findByUserIdAndBookName()은 사용할 일이 없어져서 삭제해었다.→ Domain 계층에 비즈니스 로직이 들어갔다.영속성 컨텍스트의 4번째 능력 - 지연 로딩 (Lazy Loading)예시 - User를 가져오는 부분과, 도메인 로직 실행 중간에 Print 출력을 해보자@Transactional public void returnBook(BookReturnRequest request) throws IllegalAccessException { User user = userRepository.findByName(request.getUserName()); if(user == null) { throw new IllegalAccessException("사용자를 찾을 수 없습니다."); } Systme.out.println("Hello"); user.returnBook(request.getBookName()); }실행결과를 확인하면 user를 가져오는 쿼리, hello 출력 후 userLoanHistory 쿼리를 출력하는 것을 확인할 수 있다. → 시작하자마다 유저와 대출기록을 다 가져올 수 있지만 그러지 않고, 꼭 필요한 순간에 데이터를 가져온다.  이러한 것을 지연 로딩이라 한다.  @OneToMany에 fetch 옵션에 기본 디폴트 값이다 LAZY만약 한번에 가지고 오고 싶다면 LAZY → EAGER를 사용하면 된다.@OneToMany(mappedBy = “user”, cascade = CadcadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)연관관계를 사용하면 무엇이 좋을까?각자의 역할에 집중하게 된다. 계층별로 응집성이 높아진다.새로운 개발자가 코드를 읽을 때 이해하기 쉬워진다. (모두 다 service단에 있다면 ?)테스트 코드 작성이 쉬워진다.연관관계를 사용하는 것이 항상 좋을까?지나치게 사용하면 성능상 문제가 생길 수 있고, 도메인 간의 복잡한 연결로 인해 시스템을 파악하기 어려워질 수 있다.너무 얽혀 있으면 A를 수정했을 때 , B ~ D까지 고쳐야할 수 있다.→ 여러부분을 생각해서 구조를 짜자!배포란 무엇인가현재 우리 컴퓨터에서만 작동할 수 있는 환경..배포란 : 최종 사용자에게 SW를 전달하는 과정 → 전용 컴퓨터에 우리의 서버를 옮겨 실행시키는 것전용 컴퓨터가 없는데..? → AWS를 빌리자!!AWS에서 컴퓨터를 빌릴 때 한 가지 알아두어야 할점!서버용 컴퓨터는 보통 리눅스를 사용한다.profile과 H2 DB똑같은 서버 코드를 실행시키지만, 우리 컴퓨터에서 실행할 때는 우리 컴퓨터의 MySQL을 사용하고,전용 컴퓨터에서 실행할 때는 전용 컴퓨터의 MySQL을 사용하고 싶다→ profile 개념 똑같은 서버 코드를 실행시키지만, 실행될 때 설정을 다르게 하고 싶을 때(DB 이외에도 다른 API, 다른 기능 등 ..)Profile을 적용해보자똑같은 서버 코드를 실행시키지만, local이라는 Profile을 입력하면, H2 DB를 사용하고 dev라는 profile을 입력하면 MySQL DB를 사용하게 바꾸자  H2 DB란 : 경량 Database로, 개발 단계에서 많이 사용하며 디스크가 아닌 메모리에 데이터를 저장할 수 있다.  메모리에 데이터를 저장하면 휘발되지만, 개발 단계에서 테이블이 계속 변경 되기 때문에 데이터가 휘발해야하고 ddl-auto 옵션을 create로 주면 테이블을 신경쓰지 않고 코드에만 집중할 수 있다.profile 설정나는 인텔리제이 유료버전이라 강사님 처럼 active profile 부분이 안뜨기 때문에 다른방법으로 진행하였다.-Dspring.profiles.active=locallocal 또는 dev로 입력해주면 된다. AWS의 EC2 사용하기AWS 가입 후 지역을 서울로 변경EC2인스턴스 - 내가 빌린 컴퓨터 (현재 0)인스턴스 시작 버튼 클릭인스턴스 유형은 컴퓨터의 사양t2.micro 선택 !회고벌써 워밍업도 마지막을 향해 달려가는 중이다!!7번째 과제에서 아직 나는 JPA에 익숙하지않아서 뭔가 직접 쿼리를 쓰는게 더 편했던 것 같다. @Query를 통해 직접 작성해주었다가 어? 이렇게하면 JPA에서 알아서 해주는구나 하고 여러번 바꾸기도 했다!근데 만약 쿼리에 조건이 너무 많을 경우에는 이름이 너무 길어지지 않을까라는 생각도 했었다 ㅎ..중간에 자꾸 테이블에 해당 컬럼이 없다고 오류가 나길래 .. 그럴리가 없는데라고 생각했지만 그럴리 있었다. 역시 컴퓨터는 거짓말을 하지 않는다 하하확실히 강의만 보는 것보다 직접 내가 생각하고 타이핑하는 것이 내가 어느정도까지 실제적으로 이해했는지 확인할 수 있는 길인 것 같다.앞으로 남은 마지막 미니 프로젝트까지 잘 마무리하면서 개념 부분을 더 공부해보자!

백엔드워밍업백엔드스프링

공부하자

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

워밍업 회고록 2번쨰막연하게 알고 있던 ‘스프링빈’ 에대해서 공부할 수 있었던 주였다.먼저 스프링 빈이란?서버가 시작되면, 서버 내부에 거대한 컨테이너를 만든다. 그리고 만들어진 컨테이너 안으로 우리가 생성했던 클래스들이 들어가게 된다.이때 클래스들은 다양한 정보와 함께 컨테이너로 들어가며, 인스턴스화가 이루어진다.스프링 컨테이너 안으로 들어간 클래스를 SpringBean 이라고 한다.강의내용과 코드를 통해서@RestController 어노테이션을 이용하여 우리가 만들었던 컨트롤러를 스프링빈으로 손쉽게 등록할 수 있었다.(@RestController 이전까지 사용했던 JdbcTemplate은 JPA 의존성을 통해서 자동으로 스프링컨테이너에 등록되어 있었다.)이번 섹션을 JdbcTemplate에 의존하는 컨트롤러를 3가지 역할(Controller-Service-Repository)로 Layered Architecture 분리가 가능했다.JPA 이용SQL 문을 이용한 한계를 극복하기 위한 JPA에 대해 공부를 하였다.JPA 는 API 이기에 우리가 실제로 코드로 해당 규칙대로 동작 할 수 있게 작성해주어야 한다. 이런 JPA 에서 가장 유명한 프레임 워크가 Hibernate 이다. 출처: 서버올인원 강의 JPA를 이용하기 위해 먼저 @Entity 어노테이션을 이용하였다. 해당 어노테이션은 스프링을 User 클래스에 사용한다면 DB의 User 테이블을 같은 것으로 바라보할 수 있는 기능을 수행할 수 있게 된다.이런 JPA를 사용하여 이전에 작성했던 코드들을 전더 깔끔하게 분리가 가능하였다.첫주와 같이 역시 자율적 진도를 나가는것보다 스터디라는 강제성이 부여되어 정해진 분량을 차근차근나갈 수 있어 매우 효율적이였다.

강호연

[인프런 워밍업 스터디 클럽 1기_FE] 리액트 3번 미션

과제 git Hub링크 readMe에 더 자세히 적어둠 인게임 느낌의 포켓몬 도감으로 디자인 수정함방향키로 위아래 가능밑에서 15마리 정도 될 때 추가 데이터 페칭 (무한 스크롤)차트로 스텟 상태 나타냄상세페이지의 반짝이 버튼 누르면 사진이 이로치로 바뀜 (영상 안 찍었네;;)데이터 한글화함 스펙React, Vite, tsReact-queryEmotionRecharts  설명인게임 포켓몬 도감을 모티브로 구현을 해봤습니다.  프로젝트 주목표API를 분석하여 자원을 효율적으로 분배react-query의 useInfiniteQuery를 활용한 무한 스크롤 구현rechart 차트라이브러리를 활용한 데이터 시각화Emotion을 활용해 공통 컴포넌트를 스타일드 컴포넌트로 구현하여 활용성을 높임forwardRef를 활용해 부모요소에서 자식 요소의 스크롤 이벤트를 관리 프로젝트 목표 상세API를 분석하여 자원을 효율적으로 분배이번 미션에서 가장 오랜 시간 고민한 부분이 이 부분이다. 대부분 참가자는 무지성으로 pokeAPI에 /pokemon/id로 fetch를 보내서 이름을 받아오고, 반복문을 돌려 species에서 사진을 받아와서 메인페이지를 랜더링한 걸로 알고 있다. 결론부터 말해, 이 방식은 굉장히 비효율적이다. /pokemon/id는 건당 대략 13,000 줄의 응답이 오며, species 역시 1,300 줄 정도의 응답이 온다. 따라서, 이름이나 사진 하나 받기 위해 요청을 보내는 건 효율적이지 않다. api를 분석해본 결과 /pokemon 경로에 요청을 보내 이름만 받아오는 경우가 가장 효율적인 것을 알아냈다. 이는 20 마리의 포켓몬 이름에 대해 140 줄 정도의 간결한 응답이 오므로 위의 두 요청에 비해 굉장히 효율적이다. 또, 사진이나 포켓몬의 데이터를 찾아보면 꽤나 다양한 사이트들이 나오며, 데이터에 규칙성이 있어 외부 사이트에서 사진이나 데이터를 갖고 오는 것도 충분히 가능하다. react-query의 useInfiniteQuery를 활용한 무한 스크롤 구현useInfiniteQuery를 통해 구현했다. 페이지 당 포켓몬이 10마리 정도 등장하는 걸 감안하여 하단에서부터 15 마리 정도 위에서 추가 쿼리를 날리도록 제작했다. 쿼리의 트리거가 선택된 포켓몬이므로 스크롤을 직접 조작하는 경우에는 작동하지 않는다. (추후 수정)useEffect(() => { const handleFetchNextPage = () => { if (!pokemonList) return const idx = pokemonList.indexOf(selected) if (idx > pokemonList.length - 15) { fetchNextPage() } } //useInfiniteQuery handleFetchNextPage() }, [fetchNextPage, pokemonList, selected])rechart 차트라이브러리를 활용한 데이터 시각화 stat 데이터를 받아와서 가공한 뒤 rechart의 Radar를 활용하여 데이터를 시각화했다function StatChart({ stat, color }: StatChartProps) { const data = dataRefine(stat) return ( <Container> <RadarChart outerRadius={150} width={400} height={400} data={data}> <PolarGrid /> <PolarAngleAxis dataKey="subject" /> <PolarRadiusAxis domain={[0, 1]} angle={30} tick={false}/> <Radar dataKey="A" stroke="#8884d8" fill={Colors[color]} fillOpacity={0.7} /> </RadarChart> </Container> ) }Emotion을 활용해 공통 컴포넌트를 스타일드 컴포넌트로 구현하여 활용성을 높임이전 JS 파트와 react 미션 1에서 만든 기본 컴포넌트들을 emotion으르 수정했다. 5. forwardRef를 활용해 부모요소에서 자식 요소의 스크롤 이벤트를 관리 선택된 포켓몬을 상위 컴포넌트에서 관리하는 구조인데, 스크롤이 내려감과 동시에

코파

fetch와 axios 비교

웹 개발에서 데이터를 비동기적으로 가져오는 것은 매우 일반적인 작업이다. 이를 위해 주로 사용되는 두 가지 도구가 있는데, 바로 fetch와 axios이다. 참고로 이들은 구식 브라우저에서는 지원하지 않으므로 폴리필이 필요하다.Fetch 정의 :웹 브라우저 내장 API로, 네트워크 요청을 비동기적으로 처리하기 위해 만들어졌다. const res = await fetch(url); res.status; // response 코드 res.headers; // response 헤더 // response body await res.json(); // JSON 문자열을 파싱해서 자바스크립트 객체로 변환함. await res.text(); // 문자열을 그대로 가져옴.res.json() : 바디의 JSON 문자열을 파싱해서 자바스크립트 객체로 변환res.text() : 바디의 내용을 문자열로 그대로 가져옴.만약 body의 내용이 JSON이 아닌데 res.json으로 파싱하면 오류가 남. fetch() 옵션method (메소드)GET, POST, PATCH, DELETE지정하지 않으면 기본 값이 GETheaders (헤더)Content-TypeAuthorizationbody (바디)자바스크립트 객체는 그대로 전달할 수 없기 때문에 JSON 문자열로 바꿔줘야 함. 장점 :웹 표준:Fetch는 웹 표준으로, 모든 최신 브라우저에서 기본적으로 지원된다.웹 표준 API로서의 Fetch는 setTimeout, console 등과 함께 자주 사용된다. 단점 :추가 구현 필요:Fetch를 사용할 때는 axios와 비교하여 수동으로 구현해야 하는 기능들이 있다.예를 들어, 요청과 응답을 중간에 가로채어 처리하는 Interceptor 기능은 직접 작성해야 한다. // 요청을 보내기 전에 수행할 작업 const requestInterceptor = (url, options) => { console.log('Request Interceptor:', url, options); // 예: 인증 토큰 추가 const modifiedOptions = { ...options, headers: { ...options.headers, 'Authorization': 'Bearer YOUR_TOKEN' } }; return { url, options: modifiedOptions }; }; // 응답을 받은 후에 수행할 작업 const responseInterceptor = async (response) => { console.log('Response Interceptor:', response); if (!response.ok) { const errorData = await response.json(); throw new Error(`Error: ${response.status} - ${errorData.message}`); } return response.json(); }; const fetchWithInterceptors = async (url, options) => { const { url: interceptedUrl, options: interceptedOptions } = requestInterceptor(url, options); try { const response = await fetch(interceptedUrl, interceptedOptions); return await responseInterceptor(response); } catch (error) { // 에러 처리 console.error('Fetch Error:', error); throw error; } }; // 사용 예시 fetchWithInterceptors('https://api.example.com/data', { method: 'GET' }) .then(data => console.log('Data:', data)) .catch(error => console.error('Error:', error));응답값 파싱:Fetch는 기본적으로 응답값을 자동으로 파싱하지 않는다.응답을 JSON 형식으로 변환하려면 res.json() 메서드를 호출해야 한다. 이는 코드 작성 시 추가 단계를 필요로 한다.fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));에러 핸들링: HTTP 오류 상태 코드(예: 404, 500)는 Fetch에서는 자동으로 에러로 처리되지 않는다.Fetch API에서는 HTTP 상태 코드가 200-299 범위가 아니어도 네트워크 요청이 성공하면 Promise를 반환한다. 따라서 상태 코드에 따라 별도로 오류를 처리해야한다. 이를 위해 res.status를 사용하여 각 상태 코드에 대한 처리를 세분화할 수 있다.export async function getColorSurvey(id) { const res = await fetch(`https://www./${id}`); if (!res.ok) { throw new Error('데이터를 불러오는데 실패했습니다.'); } const data = await res.json(); return data; }export async function getColorSurvey(id) { const res = await fetch(`https://www.example.com/${id}`); // 상태 코드에 따라 오류 메시지를 다르게 처리 if (!res.ok) { if (res.status >= 400 && res.status < 500) { throw new Error('클라이언트 오류가 발생했습니다. 요청을 다시 확인하세요.'); } else if (res.status >= 500) { throw new Error('서버 오류가 발생했습니다. 나중에 다시 시도하세요.'); } else { throw new Error('알 수 없는 오류가 발생했습니다.'); } } const data = await res.json(); return data; }  axios 정의 :브라우저와 Node.js 환경 모두에서 동작하는 HTTP 클라이언트 라이브러리axios는 HTTP 메소드 이름과 동일한 메소드를 사용하고 리스폰스 바디를 data 프로퍼티로 접근할 수 있다.  axios 옵션GET, DELETE RequestRequest body가 필요 없기 때문에 옵션을 두 번째 아규먼트로 받는다. POST, PATCH, PUTRequestRequest에 보낼 body 내용은 두 번째 아규먼트로 받고, 옵션을 세 번째 아규먼트로 받는다.별도의 파싱과정이 없이 JSON으로 변환된다. // Axios 모듈 가져오기 import axios from 'axios'; // GET 요청 (Request Body 없음) axios.get('https://api.example.com/data', { // 옵션 params: { id: 123 }, headers: { 'Authorization': 'Bearer token123' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); }); // DELETE 요청 (Request Body 없음) axios.delete('https://api.example.com/data/123', { // 옵션 headers: { 'Authorization': 'Bearer token123' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); });// POST 요청 (Request Body 있음) axios.post('https://api.example.com/data', { name: 'John Doe', age: 30 }, { // 옵션 headers: { 'Authorization': 'Bearer token123' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); }); // PATCH 요청 (Request Body 있음) axios.patch('https://api.example.com/data/123', { age: 31 }, { // 옵션 headers: { 'Authorization': 'Bearer token123' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); }); // PUT 요청 (Request Body 있음) axios.put('https://api.example.com/data/123', { name: 'Jane Doe', age: 25 }, { // 옵션 headers: { 'Authorization': 'Bearer token123' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); }); axios 인스턴스리퀘스트마다 공통되는 부분이 있으면 axios.create()으로 인스턴스를 생성한다.해당 인스턴스로 리퀘스트를 보내면 된다.const axios = require('axios'); // Axios 인스턴스 생성 const instance = axios.create({ baseURL: 'https://api.example.com/', // 기본 URL 설정 timeout: 5000, // 타임아웃 설정 (밀리초) headers: { 'Authorization': 'Bearer token123' // 기본 헤더 설정 } });// GET 요청 예제 instance.get('/data') .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); }); // POST 요청 예제 instance.post('/data', { name: 'John Doe', age: 30 }) .then(response => { console.log(response.data); }) .catch(error => { console.error('에러 발생:', error); });https://axios-http.com/docs/instance  참고 :Fetch() 함수는 원래 웹 브라우저에서만 사용할 수 있었으며, Node.js에서는 사용할 수 없었다.그러나 Node.js v17.5부터 실험적인 기능으로 Fetch가 도입되었고, 이에 따라 주의 메시지가 출력되었다.이후, Node.js v18.13부터는 이 기능이 안정화되어 더 이상 주의 메시지가 출력되지 않는다. 장점 :간편한 코드 작성 : 코드 작성이 간편하다.직관적인 문법 : 문법이 직관적이다.다양한 기능 : 인터셉터, 타임아웃, 요청 취소 등의 기능을 제공한다.에러 처리 용이 : catch 블록에서 에러 처리가 가능하다.자동 JSON 파싱 : 응답 데이터(res.data)는 자동으로 JSON으로 파싱된다.(텍스트로 파싱이 필요할 경우 res.text()를 사용하면 된다.)axios.get('https://api.example.com/data') .then(response => { console.log(response.data); // JSON 데이터, response.json() 불필요 }) .catch(error => { console.error('Error:', error); });단점 :번들 사이즈 증가 : axios는 많은 기능을 제공하지만 그만큼 번들 사이즈를 증가시킨다. 특히 번들 사이즈가 커지면 로딩 시간이 길어질 수 있다. axios의 기능 3개 :1) 인터셉터axios의 인터셉터는 요청과 응답을 가로채고 수정할 수 있는 기능이다. 이를 통해 전역적으로 요청과 응답을 처리하거나, 특정 상황에 맞게 헤더를 추가하거나 변경할 수 있다. 예를 들어, 모든 요청에 인증 토큰을 추가하거나, 응답 데이터를 특정 형식으로 변환하는 등의 작업을 수행할 수 있다. 모든 요청에 대해 인증 토큰 추가하기axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => { return Promise.reject(error); }); 응답에 대한 에러 처리하기axios.interceptors.response.use(response => { // 응답이 성공인 경우 return response; }, error => { // 응답이 에러인 경우 if (error.response.status === 401) { // 인증 오류 처리 } else if (error.response.status === 404) { // 리소스를 찾을 수 없음 처리 } else { // 기타 오류 처리 } return Promise.reject(error); });  요청 또는 응답에 대한 로깅axios.interceptors.request.use(config => { console.log('요청 시작:', config); return config; }); axios.interceptors.response.use(response => { console.log('응답 받음:', response); return response; }, error => { console.error('에러 발생:', error); return Promise.reject(error); }); 2) 타임아웃axios는 요청에 대한 응답을 기다리는 시간을 설정할 수 있는 타임아웃 기능을 제공한다.이를 사용하여 서버로의 요청에 대한 응답이 지연되는 경우, 일정 시간이 지난 후에 요청을 취소하고 에러를 발생시킬 수 있다.axios.get('/api/data', { timeout: 5000 }) .then(response => { // 성공적으로 데이터를 받았을 때의 처리 }) .catch(error => { // 타임아웃 또는 다른 에러 처리 }); 3) 요청 취소 기능axios는 요청을 취소하는 기능을 제공하여, 요청이 보내진 후에도 요청을 중단시킬 수 있다.이는 사용자가 요청을 취소하고 다른 작업을 수행할 때 유용하다.const source = axios.CancelToken.source(); axios.get('/api/data', { cancelToken: source.token }) .then(response => { // 성공적으로 데이터를 받았을 때의 처리 }) .catch(error => { if (axios.isCancel(error)) { // 요청이 취소된 경우 처리 } else { // 다른 에러 처리 } }); // 요청 취소 source.cancel('요청이 사용자에 의해 취소되었습니다.')

웹 개발JavaScriptfetchaxios