블로그
전체 4#카테고리
- 백엔드
#태그
- backend
2024. 10. 27.
0
4주차 발자국 | 인프런 워밍업 클럽 2기 - 백엔드
인프런 워밍업 클럽 2기입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다Spring SecuritySpring Security는 Spring Boot 애플리케이션에 보안 기능을 손쉽게 통합할 수 있는 프레임워크입니다. 인증(Authentication)과 권한(Authorization) 관리 기능을 제공하여 애플리케이션을 보호하는 데 사용되며, OAuth2, JWT 같은 다양한 보안 프로토콜도 지원합니다.dependencies { implementation("org.springframework.boot:spring-boot-starter-security") }@Configuration class SecurityConfiguration { @Bean fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain? { return httpSecurity .authorizeHttpRequests { authorizeHttpRequests -> authorizeHttpRequests .requestMatchers(AntPathRequestMatcher("/**")).authenticated() .anyRequest().permitAll() }.csrf { csrf -> csrf.disable() }.headers { headers -> headers.addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)) }.formLogin { formLogin -> formLogin.defaultSuccessUrl("/") }.logout { logout -> logout.logoutRequestMatcher(AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/") }.build() } } /* * NestJS에서는 strategy, Guard 를 사용하여 인증/인가처리를 구현합니다. * strategy 에서는 JWT, oauth 등의 보안 프로토콜을 정의하고 적용할 수 있습니다. * Guard를 정의하고 개별 request 위에 데코레이터로 적용할 수 있습니다 (global 적용도 가능) */ password encodehttps://velog.io/@glencode/Spring-Security-Crypto를-사용한-비밀번호-암호화시큐리티에 관한 강의를 아직 듣지 않았을 때, 패스워드 암호화를 해야하는 요구사항이 있어서 적용해보았던 내용을 기록합니다.dependencies { implementation("org.springframework.security:spring-security-crypto") }@Configuration class AuthConfig { @Bean fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } }의존성을 추가하고 config 를 정의합니다@Service class UserService( private val userRepository: UserRepository, private val passwordEncoder: PasswordEncoder, // and so on.. ) {...}/* * dto.password는 사용자로부터 입력받은 password * user.password는 DB에 encode 하여 저장된 password */ // encode val encodedPassword = passwordEncoder.encode(dto.password) // compare if(!passwordEncoder.matches(dto.password, user.password)) { throw BadRequestException("비밀번호가 틀렸습니다.") }passwordEncoder 를 주입하고 위 매서드를 사용하여 활용할 수 있습니다. /* * nodejs 에서는 bcrypt를 사용하여 구현할 수 있습니다. * const bcrypt = require('bcrypt'); */ // encode const encodedPassword = await bcrypt.hash(password, 10); // salt = 10 // compare if (!(await bcrypt.compare(password, user.password))) { throw new BadRequestException("비밀번호가 틀렸습니다.") } ControllerAdvice@RestControllerAdvice는 @ControllerAdvice와 @ResponseBody의 기능을 결합한 어노테이션으로, REST API에서 예외를 처리할 때 주로 사용됩니다.모든 컨트롤러에 대해 JSON이나 XML과 같은 형태로 일관된 응답을 반환할 수 있습니다.@ControllerAdvice는 Spring MVC에서 예외 처리, 데이터 바인딩, 모델 객체의 변환 등을 전역적으로 관리할 수 있게 도와주는 어노테이션입니다.서버 내부에서 입력 오류에 대한 경우를 전부 BadRequestException 으로 사용하고 message 를 다양하게 주고 있었는데, 실제로 에러가 발생했을 때 message가 오지 않아서 몹시 불편함을 느꼈습니다.직접 입력한 메시지를 응답에 뿌려주기 위해 검색 후에 아래와 같은 GlobalExceptionHandler 를 구현하였습니다.@RestControllerAdvice class GlobalExceptionHandler { @ExceptionHandler(BadRequestException::class) fun handleBadRequest(ex: BadRequestException): ResponseEntity> { val errorResponse = mapOf( "timestamp" to LocalDateTime.now().toString(), "status" to HttpStatus.BAD_REQUEST.value().toString(), "error" to "Bad Request", "message" to (ex.message ?: "잘못된 요청입니다."), ) return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse) } }# 에러 응답 예시 { "timestamp": "2024-10-23T04:22:59.959124", "status": "400", "error": "Bad Request", "message": "먼저 입실해주세요" } /* NestJS ExceptionFilter * 위와 동일한 작업을 하는 코드 */ @Catch(BadRequestException) export class GlobalExceptionFilter implements ExceptionFilter { catch(exception: BadRequestException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const message = exception.getResponse() as string; const errorResponse = { timestamp: new Date().toISOString(), status: HttpStatus.BAD_REQUEST, error: 'Bad Request', message: message || '잘못된 요청입니다.', }; response.status(HttpStatus.BAD_REQUEST).json(errorResponse); } } 작은 회고처음의 결심과 다르게, 강의 일정을 따라가는게 쉽지않았습니다. (게으르기 때문일까요~,,) 그래도 계속 하다보니 아주 조금...? 스프링에 대해 알아가는 느낌이 들어 정말 재밌었습니다. 꾸준히 노력해서, 프레임워크 상관없이 능숙하게 작업할 수 있는 백엔드 개발자가 되겠습니다.프로젝트에 아직 @TODO 가 많은데, 시작한 프로젝트는 돌아오는 주까지 열심히 해서 잘 마무리하고 싶습니다. 학부생 때가 HTML의 마지막이라, 타임리프 작업이 가장 오래걸렸습니다... (사실 아직도 끝나지 않았습니다)원래 쓰던 프레임워크와 비교해가며 이해하고 공부하는데, 이게 도움이 되었는지 아니었는지는 아직 잘 모르겠습니다. 아마 더 많이 사용해보는 시간이 필요할 거 같습니다. 공부법이든, 코딩 습관이든, 나만의 Best Practice 를 찾아가는 과정은 참 어려운거 같아요. 이번에 워밍업클럽을 통해 (!오랜만에!) 완강도 하고, 프로젝트도 하고, 강사님과 컨택할 수 있는 시간도 가지게 되어 정말 좋았습니다. + 온라인 세션에서 받은 이력서 피드백이 정말 많은 도움이 되었습니다. 이 자리를 빌려 감사의 마음을 전해드립니다 🙂다음번에도 이런 기회가 온다면 다른 강의로 신청해보려고 합니다. 이 과정을 기획하고 관리하신 인프런 운영자님과, a-z까지 한 과정에 깔끔하게 담아내신 강사님 정말 고생많으셨습니다! 수료식에서 보아요 ( •͈૦•͈ )
backend
2024. 10. 21.
0
3주차 - thymeleaf 정리 | 인프런 워밍업클럽 2기 - 백엔드
인프런 워밍업 클럽 2기입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다. thymeleafThymeleaf is a modern server-side Java template engine for both web and standalone environments.https://www.thymeleaf.org/타임리프는 스프링 프레임워크에서 사용하는 서버사이드 클라이언트 개발용 탬플릿 엔진입니다. 탬플릿 엔진은 html을 이용해 틀을 짜두고 서버에서 DB등으로 조회한 정보를 동적으로 끼워넣을 수 있게 작업합니다. Node.js에서는 pug나 EJS를 사용했던 기억이 있습니다. 친구집 강쥐 폴리 참고로 아래 코드를 가장 먼저 수정하면, IDE에서 타임리프 자동완성을 지원해주어서 좋았습니다. 영역 반복th:each와 th:block 문법을 통해 프래그먼트 반복을 할 수 있습니다. fragment중복되는 부분을 프레그먼트로 분리하여 삽입해서 사용할 수 있는 부분이 인상깊었습니다. React 기준으로는 컴포넌트랑 유사하다고 느꼈습니다. 내용대체th:text 문법을 사용합니다. | 문법을 사용하면 패턴? 등으로 이용이 가능한거같습니다. 이 부분 텍스트가 project.name으로 교체됩니다 이런 식으로 태그 내부에서 컨텐츠를 교체합니다. # (참고용) EJS 문법 if문th:if="${#strings.isEmpty(detail.url)}"
2024. 10. 13.
0
Node.js 개발자의 코-프링 적응기(2) | 인프런 워밍업 클럽 2기 - 백엔드
인프런 워밍업 클럽 2기입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다. h2자바로 구현된 인 메모리 DB 입니다. 애플리케이션이 종료되면 모든 메모리가 삭제됩니다.(휘발성) 프로젝트 내부에 임베드 해서 브라우저를 통해 매니징을 할 수 있다는 점이 특이했습니다. 겪은 오류 - user 테이블이 생기지 않음예전에 회사에서 user 테이블에 대해 데이터베이스를 명시하지 않아 오류가 발생한 적이 있었는데, 비슷한 상황인거 같아, 테이블 이름을 명시하였습니다.https://velog.io/@readnthink/DataJpaTest사용시-user-table-예약어-에러엔티티 정의 어노테이션@Table 와 @Entity테이블은 물리적인 정보, 엔티티는 논리적인 정보를 정의하는 걸까? 싶었는데, 아니었던 것 같습니다. 아예 목적이 좀 다른 느낌..?@Entity is useful with model classes to denote that this is the entity or table@Table is used to provide any specific name to your table if you want to provide any different namehttps://stackoverflow.com/questions/18732646/name-attribute-in-entity-and-tablehttps://www.inflearn.com/community/questions/75556 엔티티랑 테이블 어노테이션 내부에 들어가도 자세하게 테이블 옵션을 정의한다기보다는 느낌보다는 정말 bean임을 명시하기 위한 식별자라고 생각이 들었습니다. // Sequelize entity 정의 예시 @Table({ tableName: 'charge', timestamps: true, paranoid: true, createdAt: 'created_at', deletedAt: 'deleted_at', }) export class ChargeEntity extends Model implements ChargeAttributes {..} /* * node.js 의 Sequelize 라는 ORM에서는 @Table 에 이렇게 다양한 옵션이 존재해서 이런 차이가 있구나~~ 했었습니다. * 개인적으로는 이렇게 테이블에 옵션 때려박아두는 것 보다는 어노테이션(=데코레이터)을 여러개 사용하는 것이 낫다고 생각했습니다 */ @Id 와 @GeneratedValuehttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/idhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/generatedvalue@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // mysql auto_increment @Column(name = "report_id") /* column mapping */ var id: Long? = null /* spring에서 null로 보내면, DB에서 auto_increment */둘 다 엔티티 정의에서 기본키를 정의할 때 사용합니다. @Id는 말 그대로 기본키라는 것을 식별하기 위해 사용합니다. 함께 사용하는 GeneratedValue는, 기본키에 대한 생성 전략을 정의합니다. (전략 타입은 아래 표)https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/generationtype이번 수업에서는 db 에 기본키 생성을 위임하는 GenerationType.IDENTITY 를 사용하였습니다. // Sequelize PK 정의 예시 @Column({ field: 'charge_id', primaryKey: true, autoIncrement: true, type: DataType.INTEGER, }) id: number; /* * 여긴 없지만 @PrimaryKey 라는 데코레이터가 존재합니다. @Id와 같은 역할을 할 듯,, * autoIncrement: true, 외에 다른 옵션이 없었는데, @GeneratedValue 는 컨트롤할수 있는 범위가 넓은 것 같아 좋았습니다. */ @Columnhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/column@Column(name = "phone", nullable = false) /* column mapping */ var phone: String = phonename 은 말 그대로 물리 컬럼명을 이어주는 역할을 합니다. 사실 모든 경우에 대해, @Column을 해줘야 하는 줄 알았는데요, (DB에서 스네이크케이스, spring에서는 카멜케이스 )강의에서 "알아서 바꿔준다" 하고 슥 넘어간 거 같아 궁금해서 찾아보았습니다. By default, Spring Boot configures the physical naming strategy with CamelCaseToUnderscoresNamingStrategyhttps://docs.spring.io/spring-boot/how-to/data-access.html이렇게 스프링 부트에 내장되어 있는 전략에 영향을 받는 것으로? 우선 이해를 해 보았습니다.스프링이 참 딥해서,, 문서같은걸 뒤적거려도 확신이 잘 안서네요 ;ㅁ; nullable 은 default 가 true 이기 때문에 필요한 경우에만 명시해주면 됩니다!맨 위에 링크한 문서에, 각종 옵션들에 대한 default 가 나와있으니 개발할때 참고하면 좋을 것 같습니다.@Enumeratedhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/enumerated@Enumerated(value = EnumType.STRING) var category: ReportType = ReportType.valueOf(category)말 그대로, enum 타입이어야 함을 명시합니다. (옵션은 아래 표) https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/enumtype연관관계@ManyToManyhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/manytomany다대다(n-m) 관계일 때 사용합니다.n-m 관계의 경우 아래 사진처럼 1-n / n-1 이렇게 분리하는게 약간 >국룰문서에서도 @Jointable 을 사용해 중간 테이블을 지정하라고 되어 있습니다.https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/jointablemany-to-many는 정말 은근,, 사용할 일이 없었던 전적이 있어서 슥 넘어가겠습니다. @ManyToOne @OneToManyhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/onetomanyhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/manytoone@OneToMany( targetEntity = Post::class, fetch = FetchType.LAZY, cascade = [CascadeType.ALL] ) @JoinColumn(name = "user_id") var post: MutableList = mutableListOf()일대다 관계를 명시하는 어노테이션입니다. 관계를 맺고 있는 양쪽 테이블에 항상 모두 쓸 필요는 없고, 데이터가 필요한 부분에만 사용합니다. 이런 어노테이션의 경우 @OneToMany 앞(One)에 있는게 나고 타겟이 뒤(Many) 에 합니다.위 같은 코드가 User Entity 클래스 내부에 작성된 내용이라면, User(1) - UserTime(N) 입니다. 개인적으로 ORM의 관계는 요걸 헷갈리지 않는거부터 시작이라고 생각합니다..fetch 는 엔티티에서 항상 연관된 테이블의 정보를 가져올지 아닐지 선택합니다. (N+1 관련해서는 나중에 정리하겠습니다)cascade는 참조 무결성을 위한 키워드입니다. CascadeType.ALL 은 아래 표의 나머지들을 모두 적용하겠다는 뜻입니다.The value cascade=ALL is equivalent to cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}.https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/cascadetype // Sequelize 관계 정의 예시 // user 테이블 @HasMany(() => PostEntity) posts: PostEntity[]; // post 테이블 @BelongsTo(() => UserEntity) user: UserEntity; @OneToOnehttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/onetoone@OneToOne( targetEntity = UserTime::class, fetch = FetchType.LAZY, cascade = [CascadeType.ALL] ) @JoinColumn(name = "user_id") lateinit var timeInfo: UserTime일대일 관계를 명시할 때 사용합니다. user와 user가 사용한 시간인 userTime이 1-1 관계를 맺고 있는 경우의 예시입니다. // Sequelize 관계 정의 예시 // user 테이블 @HasOne(() => UserTimeEntity) userTime: UserTime; // userTime 테이블 @BelongsTo(() => UserEntity) user: UserEntity; @JoinColumnhttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/joincolumn조인의 기준? 되는 컬럼을 지정합니다. 프로젝트데이터베이스 관련해서 정의를 추가하였습니다. 근데 이번에 블로그 쓰면서 잘못 작성하고 그런걸 좀 찾아서 다시 해야할거같습니다. ....
backend
2024. 10. 06.
0
Node.js 개발자의 코-프링 적응기(1) | 인프런 워밍업 클럽 2기 - 백엔드
인프런 워밍업 클럽 2기입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다. 소개📌 Node.js (NestJS)를 주로 사용하는 2년차 백엔드 개발자입니다.📌 스타트업을 다니던 중 회사가 폐업하여 다시 취업 준비를 하고 있습니다.📌 스프링을 시작한 이유는 크게 2가지입니다.1. 프레임워크의 제한을 받지 않는 백엔드 개발자가 되고 싶습니다. 2. 대한민국에서 개발자로 큰 회사로 가려면 Node.js는 약간 한계가 있다고 느꼈습니다.📌 적당히 게으르고 적당히 노력하는 평범한 사람이지만 최선을 다해 완주하겠습니다.NestJS 가 약간 스프링의 구조를 따라한?? 느낌이라서 비슷한 부분이 아주 많습니다.. 강의 내용을 전부 요약하기보단 생각에 남았던 부분만 정리했습니다.웹 프레임워크웹 프레임워크는 개발을 하면서 공통되고 반복되는 작업들에 대해 좀 더 편리하게 사용할 수 있게 만들어주는 도구입니다.지금 제가 사용하는 NestJS 같은 경우에도 Node.js 를 이용한 웹 서버 개발을 편하게 만들어주는 웹 프레임워크라고 할 수 있습니다. 마찬가지로 Java나 Kotlin을 이용해 웹 서버를 개발하는 경우 Spring 이나 Spring Boot 라는 프레임 워크를 사용합니다.SpringMVC 패턴을 사용합니다.레이어드 아키텍쳐를 사용합니다. 레이어드 아키텍쳐소스코드의 역할과 관심사에 따라 계층으로 분리한 아키텍처 입니다.출처 | https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html여기서 Persistence layer 를 Data Access layer라고 하기도 합니다.controller(presentation) -> service(business) -> repository(Persistence/data access) 컨트롤러는 말 그대로 컨트롤러로서의 역할서비스는 레포지토리로부터 DB에 대한 메서드를 가져와서 사용리포지토리는 DB를 조작하는 매서드를 정의 NestJS 에서도 일반적으로 이런 계층형 아키텍처를 사용하였고, 컨트롤러에 서비스 로직이 어디까지 보여지는지에 대한 논의를 했던 것이 기억에 납니다. 이런 세세한 컨벤션은 아마 개발자마다 기준이 다를 수 밖에 없는 것 같습니다.의존성 주입(DI)https://docs.spring.io/spring-framework/reference/core/beans/introduction.htmlhttps://docs.nestjs.com/fundamentals/custom-providers#di-fundamentals의존성 주입은 한 객체가 사용하는 다른 객체를 직접 만들지 않고, 외부에서 주입받아 사용하는 방식으로, 제어 역전(IoC)기술 중 하나입니다.제어 역전(IoC)은 개발자가 컨트롤해야하는 부분을 프레임워크에 위임하여, 개발자가 신경써줄 부분을 줄여주는 기술입니다. (로 저는 이해하고 있습니다.) 출처 | https://project-eclise.fandom.com/wiki/Spring_Bean_(Alpha) 스프링에서는 Bean 이라는 것을 통해서 프레임워크에 권한을 위임할 대상을 지정합니다.bean은 Spring IoC 컨테이너에서 관리하는 객체입니다. IoC 컨테이너가 Bean 을 생성할 때 종속성을 주입합니다. 의존성 주입 방식은 생성자, 수정자, 필드 방식 3가지가 있습니다만 생성자만 사용하면 된다는 친구의 말을 들었던 기억이 납니다..(ㅎㅎ..)NestJS에서는 default(provider), request, transient 이렇게 세가지 스코프로 DI를 지원하는데, 마찬가지로 여기도 provider 방식만 사용했었습니다.이유는 둘 다 provider 방식이 안전하기 때문이라고 요약할 수 있겠습니다.JPA자바 ORM 기술의 표준 인터페이스입니다. ORM은 객체-관계 매핑으로 관계형 데이터베이스를 객체와 매핑해주는 기술을 말합니다.ORM은 만능인가? 에 대해 생각해보는 아티클https://yceffort.kr/2021/07/dont-use-nodjs-orm저수준: 순수JDBC, 중간수준: Querydsl, 고수준 JPA 로 대체해서 생각하면 됩니다 영속성 컨텍스트개인적인 이해에 도움이 되었지만 차마 똑같이 설명할 엄두는 안나는 아티클https://msolo021015.medium.com/jpa-persistence-context-deep-dive-2f36f9bd6214DB 작업이 실제로 DB에 반영되기 전에 모여있는 장소입니다. 트랜잭션이 시작되면 영속성 컨텍스트에 의해 조회/수정될 엔티티를 보관하고 관리합니다.(1차캐시) 캐시되기 때문에 성능에 도움이 됩니다(더티체킹) 스냅샷을 이용해 데이터 일관성을 향상시킵니다.(쓰기지연) 영속성 컨택스트 내에 모아놨다가 트랜잭션 종료될 때 한번에 수행합니다. (개인공부1) JVM이란JVM (Java Virtual Machine)은 Java 애플리케이션을 실행하기 위한 가상 머신입니다.Java와 Kotlin 같은 언어는 소스 코드를 작성한 후, 컴파일되면 바이트코드(bytecode)라는 중간 언어로 변환됩니다. 이 바이트코드는 운영 체제에 종속되지 않고, 다양한 플랫폼에서 실행될 수 있습니다. 운영 체제에 종속되지 않고, 다양한 플랫폼에서 실행되는 이 특징을 WORA(Write Once, Run Anywhere)라고 합니다. (개인공부2) Kotlin과 TypeScript의 차이출처 | https://itnext.io/typescript-or-kotlin-for-a-backend-268191b56525typescriptJavaScript에 정적 타입 시스템을 추가한 언어JavaScript로 컴파일되어 브라우저와 서버(Node.js)에서 실행됩니다.정적 타입이지만 any 사용 가능OOP, FP, 일급객체, 람다식 지원비동기: Promise, async/await kotlinJava 의 대안으로 나온 언어JVM을 통해 바이트코드로 컴파일되며, Java와 상호 운용이 가능합니다.엄격한 정적 타입 시스템OOP, FP, 고차함수, 람다식 지원, 함수형 프로그래밍 더 좋다 함비동기: Coroutines, suspend (개인공부3) Spring과 Spring Boot의 차이출처 | https://codestates.com/blog/content/스프링-스프링부트스프링엔터프라이즈 애플리케이션 개발을 위한 프레임워크외부 서버 필요XML 등을 이용해 환경 설정 작업 필요한땀한땀 짜야 해서 복잡도가 높다스프링부트스프링 프레임워크를 더 편하게 쓰기 위한 프레임워크 (프레임워크의 프레임워크)내장 서버 제공(Tomcat, Jetty)application.properties나 application.yml 을 사용하여 간단하게 환경 설정 가능Spring Initializr와 같은 프로젝트 시작 도구많은 설정 자동화되어 있음 미니프로젝트https://github.com/yujiniii/toy-box/tree/main/sky-kongkong 여기서 업데이트됩니다!마무리JVM과 spring 생태계가 생각보다 깊어서, 차곡차곡 개념을 추가해야할 거 같습니다. 스레드 관련해서도 정리하고 싶었는데 결국 못했네요 ᵕ_ᵕ̥̥코드 작업에서는 NestJS 와 비교되는 부분(데코레이터-어노테이션, 인터셉터 등) 을 위주로 작성하고, 프로젝트도 얼른얼른 작업 해야할 거 같습니다..다음 주차는 면접준비랑 겹쳐서 걱정이지만.... ( っ °、。)っ 그래두 화이티이잉...!!
백엔드