박윤영
@pyy21144557
수강평 작성수
-
평균평점
-
블로그
전체 4#카테고리
- 백엔드
![[인프런 워밍업 클럽 3기 백엔드 ]발자국 4주차](https://cdn.inflearn.com/public/files/blogs/1cf7c2e8-a36a-467e-a8aa-e9b697ad23a4/인프런.jpg?w=260)
2025. 03. 30.
0
[인프런 워밍업 클럽 3기 백엔드 ]발자국 4주차
해당 글은 ‘입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기(정보근)’ 강의 를 수강하고 작성한 내용입니다.https://www.inflearn.com/course/입문자-spring-boot-kotlin-포트폴리오/dashboard📝 강의 내용 정리[실습] 스프링 시큐리티 로그인 개발스프링 시큐리티를 사용하기 위해선 의존성 추가 필수build.gradle.kts //스프링 시큐리티 implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6") testImplementation("org.springframework.security:spring-security-test") 의존성이 추가되면 프로젝트의 모든 경로를 호출할 때 로그인을 요구함AdminSecurityConfiguration해당 설정 클래스에서 스프링 시큐리티를 사용하기 위해 필요한 Bean들을 생성BCryptPasswordEncode: BCrypt 해시 함수를 이용해 암호화된 비밀번호를 생성하는 비밀번호 인코더. 단방향 해시 함수로서 입력된 데이터를 일정한 길이의 해시값으로 변환함SecurityFilterChain: 프로젝트의 보안 필터를 체인 형태로 구성.requestMatchers(AntPathRequestMathcer(”경로”)해당 경로에 대해서만 시큐리티를 요구formLogin :관리자 정보를 저장을 할 테이블 생성 → entity/Account.classUserDetials인터페이스를 상속받아서 생성 → 해당 클래스에 어카운트에 관련된 메서드들 정의되어 있음override fun getAuthorities(): MutableCollection = mutableListOf(SimpleGrantedAuthority("ADMIN"))유저의 Role부여계정 관리자가 1명이라는 가정하에 유효계정 체크 등의 기능은 다 true로 리턴AdminSecurityService/Repository레포지퇴에서 로그인 정보로 계정 찾는 메서드 생성서비스에선 로그인 정보로 계정을 찾는 로직 작성(= 이후 검증 로직은 스프링 시큐리티 내부에 구현되어있음!)데이터이니셜라이저에 넣을 비밀번호를 테스트를 통해 얻어옴테스트코드에 BcryptPasswordEncoder를 사용해서 암호 하나 생성 → 생성된 암호 해시코드를 데이터이니셜라이저 코드에 삽입[실습] Docker로 MySQL 실행하기도커를 실행시키려면 커맨드 라인 인터페이스를 활용해야 하는데 매번 터미널에 명령어를 치는 일은 번거로움Docker Compose미리 정의된 파일을 작성해 두면 그 파일의 내용대로 도커 명령어를 생성해서 실행시켜줌도커 컴포즈에는 데이터베이스 비밀번호가 필요함데이터 베이스 비밀번호가 깃에 올라가면 안돼기 때문에 해당 파일은 로컬에서만 저장하도록docker-compose.yml 파일 설명version: '2' services: mysql: image: mysql container_name: mysql ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=dkssudgktpdy" - "TZ=Asia/Seoul" - "LC_ALL=C.UTF-8" command: - --character-set-server=utf8mb4 volumes: - /var/lib/docker/volumes/mysql/_data:/var/lib/mysql version: 도커 버전service: 현재 프로젝트의 서비스를 구성image: 이미지 이름container_name: 컨테이너 이름→ 도커 허브에서 해당이름의 이미지를 찾아서 다운받은 후 컨테이너를 생성함port호스트포트: 컨테이너포트호스트 포트: 도커 컨테이너가 돌아가는 컴퓨터의 포트컨테이너 포트: 컨테이너 내부에 있는 포트⇒ 도커 안에 컨테이너를 컨테이너 포트로 띄움, 해당 포트를 로컬의 호스트 포트와 연결enviroment환경변수 입력, 비밀번호와 시간대 설정,volumns로컬 컨테이너에 있는 디렉토리와 도커 컨테이너 내부의 디렉토리를 연결→ 그래서 만약 컨테이너를 지웠다가 다시 실행해도 같은 볼륨에 마운트가 되어서 데이터를 유지하고 계속 사용할 수 있음도커 실행방법터미널에서 실행시키기or인텔리제이(도커 플러그인이 깔려있을 시) 내부에서 실행[실습] Docker로 프로젝트 빌드하기DockerFileFROM openjdk:17 LABEL maintainer="pyy2114@gmail.com" VOLUME /tmp EXPOSE 8080 ARG JAR_FILE=build/libs/portfolio-0.0.1-SNAPSHOT.jar ADD ${JAR_FILE} portfolio-yoon.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "/portfolio-yoon.jar"], "-jar", "/portfolio-yongback.jar"] 도커 빌드 설정인텔리제이로 Dockerfile을 실행할 경우 Edit Configurations에서 아래 옵션을 넣어줍니다.Image Tag: {도커 아이디}/{이미지명}Build options: --platform linux/amd64Apple Silicon 맥북(M1 등)을 사용하는 경우 넣어주세요.Run Gradle task 'portfolio: clean'Run Gradle task 'portfolio: build'⇒ 도커 빌드 시키면 도커 이미지 파일 생성됨도커 이미지 파일을 가지고 컨테이너를 만들어서 실행docker-compose.ymlversion: '2' services: mysql: image: mysql container_name: mysql ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=dkssudgktpdy" - "TZ=Asia/Seoul" - "LC_ALL=C.UTF-8" command: - --character-set-server=utf8mb4 volumes: - /var/lib/docker/volumes/mysql/_data:/var/lib/mysql portfolio-yoon: image: pyy2114/portfolio-yoon container_name: portfolio-yoon ports: - "8080:8080" environment: - "SPRING_PROFILES_ACTIVE=docker" - "jasypt.encryptor.key=q1w2e3" volumes: - /var/lib/docker/volumes/portfolio-yoon/_data:/tmp depends_on: - mysql portfolio-yoon: 생성한 이미지 이름ports: 스프링부트 기본 포트인 8080environment스프링 프로파일 설정 → docker로 설정되어있으니 application-docker.yml이 프로파일로 실행되어서 해당 환경변수로 셋팅"jasypt.encryptor.key=q1w2e3"db패스워드가 환경변수 타입에 노출되면 안되기 때문에 jasypt라는 라이브러리로 암호화해서 전달할 예정(해당 비밀번호는 암호화 키? 값임, 실제 암호화할 데이터는 안보여질 예정)→ 암호화된 값을 application-docker.yml파일에 넣어주면 됨 datasource: username: root url: jdbc:mysql://mysql:3306/portfolio password: ENC(암호화된 비밀번호 작성) driver-class-name: com.mysql.cj.jdbc.Driver depends_on: mysql이 먼저 실행되어야 정상적으로 동작하므로 순서를 지정해줌Jasypt 구현build.gradle에 의존성 주입implementation("com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5")domain/configuration에 JasyptConfiguration 클래스 작성다 작성한 후 Dockerfile 빌드(이미지 생성) → dock-compose.yml 실행(도커실행)localhost:8080으로 접속해서 제대로 뜨는지 확인(만약 오류 발생시 이제는 인텔리제이에서 보는게 아니라 도커에서 로그 확인)✅미션[미션6] 가상 프로필을 나의 프로필로 바꾸기미션 6은 꽤나 간단한 미션이였다. 기존 datainitializer에 있는 데이터들을 나의 데이터로 바꿔놓는거였다. 그래서 관련 데이터로 바꿔놓고, 데이터 변경하는 김에 프로필 이미지도 바꿔놓았다.나름 열심히 사이즈를 조절해서 pc에선 알맞게 맞춰놨는데 모바일 화면에서는 깨져나왔다. css로 사이즈를 변경해야되나 싶다..[미션7] 배포한 프로젝트 공유하기드디어 마지막 미션이다. 수업을 따라하면서 도커 이미지 생성과 컨테이너 배포는 완료를 했었지만, 바뀐 데이터를 적용하기 위해 다시 배포를 하였다. 배포를 혼자서는 처음 해보는데 확실히 도커로 빌드를 하니 간편했다. 이미 배포한 후 다시 배포하는 순서는 다음과 같이 정했다. 도커 이미지 재빌드 → 도커 허브에 push → 구글 클라우드에서 해당 이미지 pull → 컨테이너 재실행 위의 방법대로 진행하니 무리없이 배포를 진행할 수 있었다. 구글 클라우드도 처음 써봤는데, 강의를 보고해서 그런건진 몰라도 심플하게 배포가 가능했던 것 같다. 배포를 하고 테스트를 하면서 느낀점이 있는데 페이지 로드가 너무 느리다는 것이다. 무료 버전을 써서 그런건지 정확한 이유는 모르겠지만 현재 프로젝트는 모든 페이지 로드 시간이 너무 길다. 이 이유를 분석하고 해결해보는 것도 좋은 공부가 될 것 같다는 생각이 들었다.📅4주차 회고이번주는 수업과 미션 모두 도커를 이용해 이미지 생성하고 컨테이너를 실행해서 서버를 배포하는 작업이 주 였다. 처음엔 강의 시간을 보고 따라하기만 하면 되니 쉽게 할 수 있을 줄 알았는데 처음 다뤄보는 툴과 플랫폼이다 보니 많이 허덕였다. 그래서 생각보단 배포에 시간이 오래걸렸다. 하지만 한번 하고 나니 도커에 한번 올려놓기만 하면 쉽게 배포가 되어 다시 따라해보는데도 별로 어렵지 않았다. 이래서 다들 도커를 쓰나보다.. 이제 수업과 미션은 다 끝났고 개인 프로젝트 개발만 남았다. 남은 시간동안 짬을 내어 최대한 완성해보고 싶다.마지막 발자국이여서 워밍업 클래스의 전체적인 회고를 해보자면 확실히 모니터링 해주는 사람들이 있고, 기간 등이 정해져 있다 보니 다른 인강을 들을 때보다 더 부지런하게 진행했던 것 같다. 매주 주어지는 미션들도 그 주에 배운 내용들을 복습하기 좋았다. 해당 프로젝트 덕분에 한 달을 나름 알차게 보낸 것 같아 마음이 좋다. 강의에서 배운 지식들을 토대로 실력을 더 디벨롭 시키는 좋은 경험이였다.
![[인프런 워밍업 클럽 3기 백엔드 ]발자국 3주차](https://cdn.inflearn.com/public/files/blogs/df284613-ce4b-4c4f-966a-aee7b3e1acae/인프런.jpg?w=260)
2025. 03. 23.
0
[인프런 워밍업 클럽 3기 백엔드 ]발자국 3주차
해당 글은 ‘입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기(정보근)’ 강의 를 수강하고 작성한 내용입니다.https://www.inflearn.com/course/입문자-spring-boot-kotlin-포트폴리오/dashboard📝 강의 내용 정리[실습] 공통 개발 - Exception과 Advice오류의 종류Throwable: 오류 최상위 클래스Error: 애플리케이션이 대응할 수 없는 오류Exception: 애플리케이션에서 대응할 수 있는 오류(개발자가 예외 처리를 해줘야함)UncheckedException: RuntimeException을 상속하는 모든 예외, Transactional에서 롤백의 대상이 됨CheckedException: Exception을 상속하며 RuntimeException이 아닌 모든 예외. 롤백을 하고 싶은 경우 별도로 지정해줘야하고 try-catch를 이용해 반드시 예외처리를 해줘야함관련 어노테이션@ExceptionHandler: 컨트롤러에서 던지 예외를 잡아 처리해주는 역할. 컨트롤러마다 예외를 처리하는 중복 코드를 작성할 필요없이, 같은 예외를 공통적으로 처리할 수 있음. (컨트롤러 클래스에 선언할 경우에만 동작!)@ControllerAdvice: 범위 내의 모든 컨트롤러 클래스에 @ExceptionHandler를 공통적으로 적용해줌. 컨트롤러 클래스는 세분화 되었지만, 예외는 똑같이 처리해야 할 경우 사용. 예외별로 다른 뷰를 리턴할 수 있으며 @ResponseBody를 붙여 리턴 값으로 응답할 수 도있음@RestControllerAdvice: @ControllerAdvice와 @ResponseBody가 같이 선언되어 있음코드 설명AdminApiControllerAdvice인터셉터와 비슷, 예외처리에 특화된 인터셉터컨트롤러가 예외를 던지면 해당 예외를 잡아서 대응해주는 컨트롤러@ExceptionHandler fun handleException(e: AdminException): ResponseEntity{ } 해당 메서드를 컨트롤러안에 넣어도 동작하는데, 공통된 에러 처리를 위해서 따로 빼는게 좋음(중복 방지, 관리 용이)처리할 에러 종류→3가지AdminException: 개발자가 정의하여 의도적으로 던지는 AdminException 처리용MethodArgumentNotValidException: Validation에서 던지는 예외를 처리, 클라이언트에서 보낸 데이터 오류이기 때문에 BadRequest응답Exception: 그 외 예외 처리. “시스템 오류”라는 메세지는 클라이언트쪽에 구체적인 오류를 숨기는 목적[실습] 공통 개발 - DTOApiResponsecompanion object{}: 이 안에 정의되는 메서드들은 static 메서드가 됨static으로 생성한 이유: 여러 컨트롤러에서 동일한 응답을 편하게 사용하기 위해데이터 저장, 수정, 삭제에 대한 메세지 지정FormElementDTOhtml에 input 태그에 들어갈 유형을 정해주는 클래스(텍스트, 데이트, 셀렉트)서버에서 넘겨준 form을 보고 프론트에서 어떤 형태로 form을 만들지 선택할 수 있도록⇒ 이게 여기서 화면까지 그려주기때문에 필요한 기능 같음TableDTO서비스 단에서 클라이언트로 줄 응답을 만들때 여기 DTO통해서 주면 얘가 알아서 형식 맞춰주는 DTOvarag: 그냥 “a”,”b” 형태로 줘도 [”a”,”b”] 같이 리스트로 처리해줌필드 종류name: 테이블의 이름columns: 테이블이 갖고 있는 컬럼들records: 각 레코드들동작 방식테이블의 컬럼 정보와 레코드 정보를 엔티티 클래스에서 받아옴ex) Link 테이블 값을 조회한다고 하면name: Link columns:[link_id, name, content, is_active, created_Date_time , updated_date_time] records:[ [1, "Github","", true, 날짜, 날짜], [2, "Instagram","", true, 날짜, 날짜] ] [실습] 공통 개발 - 인터셉터어드민의 사이드바 메뉴를 프론트가 아닌 서버에서 관리하기 위함 목적PageDTO: 소메뉴, 메뉴명과 해당 페이지로 이동하는 경로를 가짐MenuDTO: 대메뉴, 메뉴명과 포함된 소메뉴를 가짐AdminInterceptor위의 MenuDTO와 PageDTO를 생성하여 Model에 넣어주는 역할AdminInterceptorConfiguration인터셉터를 설정/admin 이하의 모든 경로에 대해 동작하도록[실습] 조회 개발 - 연관관계 없음서비스에서 repository를 바로 주입해서 쓰는것과 이전의 퍼싸드 패턴 둘중에 뭐가 더 좋을건가..서비스에서 DTO로 변환할때 TableDTO 사용TableDTO안에 만든 from 메서드가 클래스 info와 entity만 넣으면 dto로 변환해줌controller먼저 Form 요소 세팅 → model에 넣어줌서비스에서 테이블 정보 받아오기 → model에 넣어줌[실습] 조회 개발 - 연관관계 있음getExperienceDetailTable 메서드 설명모든 experienceDetail 을 다 조회해서 화면에 뿌려주지 않고, 컨트롤러에서 상세조회 값이 true인 애들만 상세조회 버튼이 노출되고, 해당 버튼을 누르면 여기 메서드로 들어와 디테일 값을 넘겨주는 방식(매개변수 id가 nullable한 이유: 상세조회 버튼 누르기 전에 빈 리스트를 먼저 주기 위해서)[실습] 삽입, 수정 API 개발 - 연관관계 없음@NotBlank: 해당 필드가 비어있지 않아야 함을 나타냄, 비어있으면 예외 발생두번째 toEntity 메서드 → 엔티티 수정 용(엔티티 수정 시엔 해당 메서드 호출)컨트롤러에서 데이터 받아올 때, data class로 DTO받아온 다음에 해당 데이터를 toEntity()메서드를 이용해서 엔티티형으로 변환, 해당 엔티티 값을 레포지토리에 저장[실습] 삽입, 수정, 조회 API 개발 - 연관관계 일대다valid 종류@field:Positive값이 0보다 커야함@field:Min(value = 정수), @field:Max (value = 정수)값의 최대, 최솟값 정의val detailMap = experience.details.map { [it.id]() to it }.toMap(){id: experienceDetail} 형식으로 만들어줌experience서비스의 update메서드에 repository.save()메서드를 호출하지 않는 이유 → JPA더티체킹에 의해 트랜잭션이 종료될 때 업데이트가 자동으로 반영됨.✅미션[미션4] 조회 REST API 만들기일단 로그인 없이 게시글을 조회해오는 부분만 구현했다. 게시물 조회는 목록 조회와 상세 조회 두가지를 구현했다코틀린으로 구현을 하다보니 에러처리를 어떻게 할지 몰라 애를 좀 먹었었다. 현재는 exceptionHandler를 이용해 IllegalState예외만 잡아놓게 처리해놨는데, 강의를 보니 여러 에러들과 200코드를 한번에 처리하는 방법이 있었다. 미션 후 적용해 볼 생각이다.미션 주제에 한 api에 테스트를 3개 작성하라는 내용이 있었다. 어떤 테스트를 진행할까 고민하다가, 일단 각 기능이 제대로 동작하는지 성공 테스트 2개와 존재하지 않는 아이디로 조회를 할 때 발생하는 예외 테스트를 진행해 보았다. 기능이 좀 더 구체화 되면 그에 따라 테스트도 늘 예정이다.[미션5] 삽입, 수정, 삭제 REST API 만들기삽입, 수정, 삭제 기능을 구현하려고 보니 조회기능 구현할땐 발목을 잡지 않았던 로그인이 문제였다. 원래 계획은 스프링 시큐리티를 붙이는 거였어서 붙이고 나면 현재 로그인된 멤버값을 받아오는건 일이 아니였는데 로그인이 구현되어있지 않는 상태에서 멤버값을 어떻게 가져오지가 걱정이였다. 맨처음 삽입 기능만 구현할 때는 임시로 Path에 받아오도록 구현했었는데 아무리 임시라고 해도 보안상에 좋지 않을꺼라는 생각이 들었다. 그래서 그나만 안전한 RequestBody에 memberId를 받아오는 걸로 구현해 놓았다.삽입기능은 많이 복잡한 기능이 아니다보니 수월하게 작성할 수 있었는데 수정에서 문제가 발생했다. 강의에선 더티체크를 통해 값을 save하지 않아도 jpa가 레포지토리에 업데이트를 진행한다고 했었는데 테스트를 해보니 값이 변경되지 않았다. 이유를 찾아보니 메서드위에 Transactional 어노테이션을 누락했었다. 어노테이션 추가하고 나선 값이 제대로 변경되는 것이 확인되었다.삭제 기능에서도 memberId를 전달하는 것이 문제가되었다. 수정이나 삽입은 body값이 존재하니 거기에 숨겨서 보내면 됬는데 삭제에서 requestBody를 쓰는건 Rest에 위반이 될것같았다. 그래서 하는 수 없이 삭제에서는 param값으로 memberId를 받아오는 형식으로 구현해놓았다.테스트 작업은 일단 각 기능들이 정상적으로 동작하는지와 프로젝트 주제에 관리자와 사용자 권한 내용이 있기때문에 현재 사용자가 권한이 있는 사용자인지를 테스트하는 식으로 진행하였다.📅3주차 회고강의에서 어드민 개발을 시작하면서 확실히 구현하는 기능이 많아졌다. 그리고 DTO를 통합해 데이터를 한번에 변환하는 것과 에러와 성공 관련 상태코드들을 한곳에서 처리하는 부분도 다뤄져있어서 유익했다. 그동안 프로젝트에서는 예외처리 부분 코드를 가져와서 사용했었는데, 이번 기회에 직접 작성해보니 핸드러 흐름을 알기에 좋았던 것 같다.미션을 진행하면서 아쉬운 부분이 많이 느껴졌다. 좀 다급하게 개발을 하다보니 구현하면서도 이게 맞아..? 하는 부분들이 여럿있었고, 좀더 기능을 구체화해서 리팩토링을 해봐야겠다는 생각이 들었다. 어찌보면 간단한 기능들인데도 막상 구현하려니 쉽지는 않아서 힘들었다. 그래도 하나씩 기능이 구현될 때마다 재미는 있었다. 워밍업 클럽이 끝나고 나서도 기능 구체화를 위해 개발과 리팩토링을 꾸준히 진행할 예정이다.
![[인프런 워밍업 클럽 3기 백엔드 ]발자국 2주차](https://cdn.inflearn.com/public/files/blogs/aee50eda-705a-499f-9634-891043deb644/인프런 이미지.png?w=260)
2025. 03. 16.
0
[인프런 워밍업 클럽 3기 백엔드 ]발자국 2주차
해당 글은 ‘입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기(정보근)’ 강의 를 수강하고 작성한 내용입니다.https://www.inflearn.com/course/입문자-spring-boot-kotlin-포트폴리오/dashboard📝 강의 내용 정리[실습] 데이터베이스 초기화@component 어노테이션스프링 빈에 등록시킬 클래스라고 알려줌그외 @controller, @service, @repository도 컴포넌트 중 하나(원본코드가면 component가 붙어잇음)@Profile(value=[”default”])프로필이 디폴트일때만 해당 클래스를 빈으로 등록해라(개발용 프로필 데이터 용)생성자 주입시먼저 등록시켜야할 클래스들 먼저 등록시킴예를들어 현재 클래스에서 생성자 주입으로 레포지토리 클래스들을 주입 받으면스프링은 해당 레포지토리 클래스들을 먼저 만들고 그다음에 해당 클래스를 생성@PostConstruct스프링 빈 주입 후 해당 어노테이션이 붙은 메서드들을 한번 더 실행하면스프링 초기 빌드 끝나는거임(찾아봐 인터넷에 확실X)출력문으로 해당 메서드가 언제 타는지 확인해보면→ 스프링 빈들 적용 완료됐다는 로그 뒤에 찍힘→ 해당 메서드 실행되고 난후에 톰캣 열림 📌실제 프로젝트는 출력할때 print쓰지 말고 log 사용하셈레포지토리 클래스의 saveAll 메서드인터페이스인 JpaRespository 에 정의되어있음 그래서 우리가 따로 만들지 않았더라도 쓸 수 있음(매우 기본적인 쿼리문은 jpa가 미리 정의해놔서 가져다 쓰면 됨)experienceRepository.saveAll()로 데이터를 저장하는데이렇게 하면 experience랑 experienceDetails랑 연관관계라저거 saveall만 해도 experienceDetails에도 삽입 쿼리가 날라감(이거가 엔티티에서 cascadeType을 지정해줬기 때문)=⇒ 쿼리문 로그에서 확인 해보셈[실습] 리포지토리 개발레포지토리 리턴값이 여러개 올것같으면 List형으로 받아오기하나만 들어올때면 Optional로 받아오기override fun findById(id: Long): Optional이거 jpa에 기본있는 메서드 명인데 다시 만들려면 앞에 override 붙여줘야함 📌프로젝트 클래스랑 익스페리언스 클래스 같이 연관관계가 있는 클래스들은 현재 코드로는 성능이 좋지 않음 다음 시간에 성능 개선용 코드를 다시 짤 것 같음[실습] 리포지토리 테스트 코드 작성테스트 코드 작성 이유큰 서비스 일 수록 의도하지 않은 결과가 나올 수 있기 때문에 테스트 코드를 촘촘하게 작성하여 서비스 전체 기능에 미치는 사이드 이펙트를 사전에 감지하는 것이 좋음테스트 코드 작성법일반 class테스트 코드를 작성할 클래스를 오른쪽 마우스 클릭Generate → Testtest 패키지에 해당 테스트 클래스가 생성됨인터페이스(ex. 레포지토리)테스트 패키지 안에 직접 만들어줘야함테스트할 인터페이스 이름에 +Test 이름으로 클래스 생성(코틀린 클래스)생성된 클래스 위에 어노테이션 추가@DataJpaTest : jpa 관련 테스트를 위한 설정 제공@TestInstance: 테스트 인스턴스의 라이프 사이클 지정. 각 메서드 마다 인스턴스를 생성하는게 아닌 모든 테이스에 적용될 작업을 beforeall 로 한번만 수행할 것이기 때문(—> 이건 좀 더 찾아봐야될)https://devs0n.tistory.com/40@BeforeAll : 테스트 클래스 내의 모든 @Test 메서드 실행 전에 한 번 실행됨의존성 주입은 클래스() 안에 @Autowired로 주입테스트할 메서드에 @Test 어노테이션 달아야 테스트로 인식 📌assertThat( import assertj) 사용: 더 직관적ctrl+ r 로 replace로 코드 한번에 변경 가능[실습] 리포지토리 성능 개선N+1 문제현재 테스트 코드에서findAllByIsActive() 테스트를 돌려보면 로그에 굉장히 많은 쿼리문이 나가는 것을 확인할 수 있다⇒ JPA N+1 문제부모꺼 부르고 자식 것도 다 불러오는 문제코드에서는테스트 메서드 안에 있는 for문 돌때 experience.details 찾을때마다 쿼리가 날라감→ 이유: 엔티티클래스에서 fetch타입이 lazy였기 때문에⇒ 이걸 만약 eager로 바꾼다면→ for문 말고 findBy 할때 여러개 쿼리가 날라감(lazy랑 eager랑 이 차이인거)⇒ jpa proxy가 fetch 타입이 lazy인 경우 가짜 객체 상태로 있다가, 해당 객체가 호출됬는데 데이터가 없을 경우 쿼리를 날려서 그때 값을 가져오는거해결법fetch joinjpa에 의존하지 않고 직접 jpql쿼리를 보냄→ 쿼리가 하나만 나감.(Good~)하지만 해당 방법은 onetomany나 manyto many관계의 자식 엔티티가 여러개일 경우 하나만 조인할 수 있다는 한계가 존재함 📌조인 테스트한쪽에만 데이터가 있는경우엔 left join 사용그냥 join은 양쪽에 다 데이터가 있어야지만 가져올 수 있음batch fetch sizeex) ProjectRepository는 여러개가 참조되어있어서 1로 안됨단. project랑 p.skill이랑 조인하고 여기서 s.skill이랑 조인은 됨일단 자료에 있는 쿼리문 적은다음에 테스트 실행해보면 쿼리문이 여러개 나감 → 프로젝트랑 스킬즈는 join이 되었는데 디테일즈는 조인이 안되서 detail 호출할때마다 쿼리가 날라가는거래(=lazy)⇒ 즉 여러개 조인은 못가져옴이걸 해결하려면 properties에서 default_batch_fetch_size를 수정 해야함→ 이렇게 하면 나가는 쿼리가 줄음, 단 in으로 한번에 가져옴이거는 n번 나갈 쿼리를 batch size를 키워서 한번에 in으로 여러개 가져옴1+N 이 1+(N/batch_ferch_size) 로 줄일 수 있다아결론적으로 쿼리문을 줄일 순 있다아 해당 페이지 어노테이션 정리@Controller컴포넌트 스캔의 대상이 되어 빈으로 등록됨. SSR(서버 사이드 랜더링) 방식으로 웹 개발 시 사용됨. 스프링 내부적으로 return된 문자열과 같은 이름을 갖는 html파일을 찾아 클라이언트에게 응답@RestController컴포넌트 스캔의 대상이 되어 빈으로 등록됨. CSR(클라이언트 사이드 렌더링) 방식으로 웹 개발을 하거나, 데이터의 처리만을 담당하는 API를 개발할 때 사용됨. return되는 값은 그대로 HTTP 응답 메시지의 Body에 들어감. String 외의 타입으로 return 시 HTTP 헤더 값 설정에 따라 JSON으로 변환하여 Body에 넣음@RequestMappingHTTP요청을 정의하는 역할. 클래스와 메소드에 붙일 수 있으며 클래스에 붙일 시 해당 경로가 클래스 내부 모든 메소드에 공통적으로 적용됨@GetMapping@RequestMapping(method = [RequestMethod.GET], name =”/test”) 와 같고, 해당 경로로 GET요청을 했을 때 해당 Method가 호출됨.@Service컴포넌트 스캔의 대상이 되며 서비스 레이어에 해당함을 명시@Repository컴포넌트 스캔의 대상이 되며 리포지토리 레이어에 해당함을 명시@Component컴포넌트 스캔의 대상이 되어 빈으로 등록됨[실습] 클래스 생성@Controller와 @RestController차이점@Controller해당 컨트롤러 내부로 들어와서 path 메서드의 리턴값과 동일한 이름의 html파일이 스프링 내부에 있으면 해당 페이지를 로드해줌ex)@Controller class ControllerClass{ @GetMapping("/test") fun test(): String{ return "test" } } ⇒ 스프링 프로젝트 resources/templates/위치에 test.html 이라는 파일이 존재하면 해당 path로 get요청 시 return값이 test이므로 test.html 페이지(view 파일)가 열림(단적인 예로 만약 return “ttest”라고 오타가 나면 못 불러옴)@RestController해당 어노테이션은 내부로 들어가면 @ResponseBody라는 어노테이션이 붙어있는데 이게 return 값을 그대로 http ResponseBody에 넣어서 돌려줌(위에@controller처럼 관련 view파일을 찾는게 아님)ex)@RestController class RestControllerClass{ @GetMapping("/test") fun test(): String{ return "OK" } } ⇒ 해당 경로로 접근하면 그냥 웹페이지에 OK 글자만 나옴@GetMapping(”/test”)= @RequestMapping(method = [RequestMethod.GET], name =”/test”)[실습] DTO 개발DTO데이터 트랜스퍼 오브젝트의 약자. 데이터를 담는 통 같은 역할[사용이유]엔티티를 클라이언트에 바로 전달하게 되면 데이터베이스에 연결된 모든 컬럼들을 모두 전달하는게 비효율적이고, 보안적 이슈도 있고, 원하지 않는데이터가 데이터베이스에 적용될 문제도 있기 때문[결론]엔티티는 내부적으로만 사용하고, 외부로 노출되는 데이터는 DTO에 담아서 전달하는것으로data class코틀린에서 제공하는 dto 전용 클래스[특징]ToString 사용시 데이터 들을 key, value 형태로 출력해줌선언할 변수들은 생성자 위치에엔티티에서 LocalDateTime형인 변수들을 DTO에서는 그냥 String형으로 사용(어차피 자바의 자료형을 클라이언트에서 사용 못함)→ 그래서 그냥 문자, 숫자 정도만 구분해서 넣어주는게 좋음[실습] 리포지토리 개발presentation 패키지의 repository 클래스들디자인 패턴 중 퍼사드 패턴의 기능이전의 도메인 패키지의 repository클래스들은 데이터베이스랑 직접적으로 상호작용하는 클래스이고현재 패키지의 repository클래스들은 도메인의 기능들을 활용해서 presentation 레이어에 필요한 기능들을 한번 더 랩핑해서 사용하는 목적→ 즉 presentation 레이어에 있는 서비스 클래스들이 도메인 패키지의 레포지토리들을 의존성 주입을 각각 받는걸 막고, 해당 레포지토리 클래스에서 묶어서 주입 받을 예정해당 레포지토리 클래스는 도메인 패키지의 레포지토리들을 랩핑하는 역할이다[실습] 서비스 개발readOnly: 읽기 전용, jpa의 더티 체킹 등을 수행하지 않아 약간 성능상의 이점이 있음 → 조회만 하는 메서드는 붙여주는게 좋음[실습] 서비스 테스트 코드 작성단위테스트테스트 하는 기능에 더 집중하여 하나의 기능만 테스트[Mockito]mockito를 사용하려면 클래스 위에@ExtendWith(MockitoExtension::class)를 붙여야함@InjectMocks만든 mock을 주입할 대상(=테스트 할 대상)lateinit코틀린은 null을 허용하지 않기 때문에 초기화가 필순데, lateinit을 하면 초기화를 늦추는 작업. (mock이 생성된 다음에 초기화 할 목적)@Mock이 붙은 레포지토리로 데이터를 만들어서 그걸로 서비스 테스트 진행[실습] 컨트롤러 개발api 컨트롤러는 데이터의 전달에만 집중클라이언트 사이드 랜더링(SSR) 방식으로 개발 시 프론트엔드 개발자는 프론트엔드쪽 프로그램을 별도로 만들고 서버쪽은 데이터 처리에만 집중하고 둘이 데이터만 주고받음뷰 컨트롤러서버사이드렌더링서버에서 프론트 작업까지 다하고 결과물인 html파일만 전달[api 컨트롤러]api 버전 관리를 위해 path에 버전명 명시 → 버전링컨트롤러를 웹브라우저에서 호출해보면 리턴한 DTO값들이 JSON포맷의 스트링으로 바껴서 브라우저에 나타남주로 JSON으로 데이터 주고받음[view 컨트롤러]해당 컨트롤러에서는 데이터를 리턴하는게 아니고 html이름을 반환할꺼임서비스에서 받은 데이터들은 model.addAttribute에 담아놓으면 됨model.addAttribute(”key”, value)⇒ 나중에 html에서 해당 키값 불러오면 되겠군.이 방법을 사용하려면 패스 경로에 해당 html이 존재해야만 함.[실습] 컨트롤러 테스트 코드 작성API 컨트롤러 테스트통합 테스트스프링부트를 다 띄운다음에 api를 직접호출해서 결과 검증@SpringBootTestSpringBoot어플리케이션을 테스트하는데 사용. 실제 어플리케이션과 유사한 환경을 구성하여 테스트를 실행@AutoConfigureMockMVCSpringMVC를 모의로 테스트하는데 사용. MockMVC객체가 자동으로 구성되어 컨트롤러를 모의로 테스트할 수 있음uri 호출 메서드performGet()MvcResult를 리턴mockMvc빌더를 이용해서.perform(MockMvcRequestBuilders.get(uri)해당 uri를 get 방식으로 호출해줌.andDo(MockMvcResultHandlers.print())호출 후 결과를 출력.andReturn()결과를 mvcResult로 리턴contentAsStringhttp Response 바디값을 String으로 파싱jsonArray스트링 값을 json배열로 파싱⇒ resume같은 경우엔 애초에 객체로 return을 받기 때문에 jsonObject로 파싱.optJSONArray를 써서 키값을 넣어주면 해당 객체의 value값을 가져옴[실습] Thymeleaf - 부트스트랩 템플릿부트스트랩 탬플릿 가져오기사용할 프로젝트 다운 후프로젝트 src/main/resources/templates 경로에presentation 폴더 생성 후다운받은 프로젝트의index.html, project.html, resume.html 복사src/main/resources/static경로에assets, css, js 폴더 복사[실습] Thymeleaf - 템플릿 수정(index)html은 동적인 언어가 아니기 때문에 공통적인 내용을 여러 페이지에 표시할 때 같은 코드를 매번 중복해서 작성해줘야함→ 이 중복을 줄이기 위해 타임리프의 프레그먼트 기능을 이용현재 코드를 fragment화index.html → fragment-navigation.htmlindex.html의 태그 안의 내용을 fragment-navigation.html 의 태그 안에 잘라넣기(원래 부분은 지워코드 내의 href에 있는 .html 제거현재 부분이 웹페이지 상단의 네비게이션 바 부분contact는 안쓸꺼니까 지우고span 태그 옆에 Start Bootstrap 이름 내 이름으로 변경th:fragment=”navigation” 추가index.html의 지워진 위치에 태그로 교체⇒ 이렇게 하면 타임리프가 th:replace를 보고 해당 경로에 있는 파일을 찾아 그 안에서 해당 이름의 프래그먼트 조각을 가져와 해당 위치에 넣어줌head, footer, dots 부분 위와 동일하게 fragment화 진행About 섹션 수정여기에 텍스트들은introductions의 요소들을 한줄한줄 표시하는 형태로 진행타임리프의 th:each 를 이용해 데이터베이스에서 받아온 값을 동적으로 html 에 표시[about의 텍스트 데이터(introductions) 수정] 텍스트값${introductions} 에서 받아온 값들을 introduction에 하나씩⇒ 컨트롤러에서 model에 넣어줬던 데이터임.th:text 에서 데이터로 들어온 introduction.content 값을 오른쪽 텍스트값에 넣어줌[about의 링크 데이터(links) 수정] th:href 받아온 데이터 값으로 href=””값 변경해줌는 아이콘 인데 아이콘 이름도 link.name으로 변경target=”_blank” 이걸 넣어주면 링크가 새 탭에서 열림타임리프 문법 미리보기th:if : 조건이 참일 경우 해당 HTML요소를 표시하고, 거짓일 경우 표시하지 않음Experience카드를 fragment로 변경공통사용 부분을 한번더 공통화layout(~{::#content})#content : id가 content인 태그를 저 안에 넣어줌id 값들은 태그에 넣어줄꺼임[실습] 인터셉터 개발인터셉터컨트롤러 보다 앞단에서 동작컨트롤러의 요청을 한번 잡아서 공통적인 처리를 할 수 있게 해주는 기능@Configuration: 설정값이 들어있는 Beanctrl + o: 오버라이드 할 수 있는 메서드 목록나옴preHandle(): 컨트롤러 요청 전 동작postHandle: 컨트롤러 응답 후 동작, 만약 컨트롤러가 예외를 발생시키거나 정상적으로 동작하지 않았으면 동작하지 않음afterCompletion:컨트롤러 응답 후 동작, 컨트롤러의 성공 여부와 상관없이 항상 동작✅미션[미션1] 깃허브 리포지토리에 프로젝트 올리기뻔한 주제이기는 하지만 로그인을 한 유저들끼리 글을 쓰고 읽을 수 있는 커뮤니티 게시판을 프로젝트 주제로 잡았다.일단 기본적인 틀은 모든 사용자가 글을 올릴 수 있는 자유글 있고, 관리자만 올릴 수 있는 공지글이 있을 예정이며, 관리자가 게시판의 글을 관리할 수 있도록 구현할 예정이다.[미션2] 테이블 설계하기테이블은 크게 유저, 게시판, 카테고리로 잡았다.처음엔 아무생각없이 유저정보를 아이디 비번 닉네임으로만 구성을 했었는데, 관리자가 있으려면 role필드가 필요하다는것을 알고 유저테이블에 추가하였다.그리고 공지글과 자유글을 카테고리로 구분하도록 테이블을 구성하였다.추후 글 카테고리를 자유롭게 생성시킬 수 있도록 확장성을 염두해 두었다.[미션3] REST API 설계하기기본적인 api만 설계해 놓았다. 유저 관련은 회원가입, 로그인, 로그아웃, 정보 조회와 같이 기본적인 기능등에 대해 적어놓았고,게시판 API도 기본적인 CRUD에 관한 내용만 적어두었다. 아마 개발을 시작하다보면 추가적으로 수정할 부분들이 생길 것 같다.카테고리 테이블에 관련한 CRUD는 아직 개발 예정으로 빼놓았다. 위의 기능들을 구현한 후 추가할 예정이다📅2주차 회고2주 정도 코드를 봤더니 저번주 보다는 예제 코틀린 코드들이 어느정도 익숙해진 것 같다. 그리고 이번주가 본격적으로 기능을 구현하는 진도였다보니 저번주보다 더 흥미롭게 강의를 들었다. 평소에 알고는 개념적으로만 인지하고있었던 N+1문제도 직접 비교를 보여주시면서 강의를해주시니 더 와닿았던 것 같다. 테스트 코드 부분은 아직 익숙하진 않아서 아직 어렵지만 강의에서 배운것을 토대로 미션 코드에 적용해보면 이해가 좀더 될것 같다. 타임리프 부분은 진짜 처음 다뤄봤는데..확실히 html은 나한테 맞지않았다..(코드가 너무 많아..) 그래도 화면이 직접보여지다보니 뭘 만들고있다는 느낌은 확실히 들어서 재미있었다. 부트스트랩이라는 홈페이지를 처음알았는데 혼자 프로젝트 할때도 손쉽게 프론트를 구현할 수 있을 것 같아 자주 이용할 예정이다.이번주에 주어진 미션들은 아직은 프로젝트 설계부분이라서 뭘 많이 하지는 않았는데, 간단한 테이블을 짜는 작업에도 혼자서 하려니 살짝 부담이 있었다. 이런작업들을 좀더 유연하게 할 수 있도록 여러번 해봐야겠다는 생각이 많이 들었다. 다음주부터는 정말 기능 구현을 시작해야하는데, 자신이 조금 없지만 차근차근 해 볼 예정이다.
![[인프런 워밍업 클럽 3기 백엔드 ]발자국 1주차](https://cdn.inflearn.com/public/files/blogs/346f67eb-a36d-4d66-88da-ffdf5fa65f94/인프런 이미지.png?w=260)
2025. 03. 09.
0
[인프런 워밍업 클럽 3기 백엔드 ]발자국 1주차
해당 글은 ‘입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기(정보근)’ 강의 를 수강하고 작성한 내용입니다.https://www.inflearn.com/course/입문자-spring-boot-kotlin-포트폴리오/dashboard📝 강의 내용 정리[이론]1. 웹서비스를 구성하는 요소클라이언트요청하는 주체 ex) 웹 브라우저들서버요청을 수행한 후 응답하는 주체일반적으로 데이터베이스에서 데이터의 삽입,수정,조회,삭제 한 뒤 결과를 클라이언트에 반환데이터베이스서버와 상호작용하는 데이터의 집합데이터의 집합을 저장하고 관리하는 역할을 하는 프로그램을 DBMS라 함DNS 서버DNS서버에 도메인 주소를 보내면 해당 IP 주소를 찾아서 알려주는 방식2. 웹 프레임워크와 Spring웹 프레임워크동적 웹 서비스 개발을 편리하게 만들어주는 도구, 공통적으로 요구되는 기능들을 보다 편리하게 사용할 수 있게함백엔드 웹 프레임워크 종류Java, Kotlin: SpringJavaScript, TypeScript: Express.js, Next.jsPython: DjangoRuby: Ruby On Rails프론트엔드JavaScript: React, Angular, Vue.jsSpring Framework자바 기반의 웹 프레임워크로 웹 서버 갤바를 편리하게 모듈화 해놓음MVC패턴Model: 데이터를 담음. Controller는 데이터를 넣고, View는 데이터를 꺼내옴View: 사용자에게 보여지는 화면을 담당Controller: 요청을 받아 작업을 수행⇒ 이 3개의 컴포넌트가 각각 독립된 기능을 담당하고 서로 상호작용하는 방식으로 구조를 잡은 디자인 패턴레이어드 아키텍쳐스프링 웹 개발 시 주로 레이어드 아키텍쳐를 기반으로 개발Presentation(Controller)클라이언트가 요청할 수 있는 인터페이스, 전달받은 데이터를 검증 후 서비스의 메서드를 호출Business(Service)목적에 맞게 데이터를 처리. 일반적으로 레포지토리의 데이터베이스 처리 메서드를 호출Data Access(Repository)데이터베이스에 접근하여 작업을 요청스프링 Bean과 의존성 주입(Dependecy Injection)스프링 Bean스프링에서 관리되는 객체(= 인스턴스)를 의미. 스프링 Bean으로 지정된 클래스는 개발자가 직접 인스턴스를 생성, 관리하지 않고 스프링 컨테이너가 관리 → 이를 제어의 역전(IoC) 라고 함의존성 주입한 객체가 사용하는 다른 객체를 내부에서 직접 만들지 않고, 외부에서 주입받아 사용하는 방법. 스프링이 실행될 때 컴포넌트 스캔이라는 과정을 거쳐 Bean으로 지정된 클래스들의 객체를 모두 만들어 준 후 스프링이 관련 Bean들을 생성해줌스프링 부트의 의존성 주입 방법생성자(권장)런타임에 수정자를 호출해서 의존성이 바뀌는것 방지순환참조 시 컴파일 오류를 발생해 런타임 중 스택오버플로우 방지의존하는 Bean이 누락되면 컴파일 오류 발생시켜 런타임 중 null 예외 방지수정자필드3. HTTP와 REST APIHTTP정의네트워크로 통신하는 두 컴포넌트 간의 통신 규약HTTP 요청/응답RequestResponseHTTP 요청 메서드GET: Read 작업 요청 시 사용, 브라우저 주소창은 항상 GET 메서드로 요청POST: Create작업을 요청할 때 사용PUT: Update 작업 요청 시 사용PATCH: Update 작업 요청 시 사용DELETE: Delete작업 요청 시 사용HTTP 상태코드200: OK정상 처리300: Multiple Choices리다이렉션400: Bad Request클라이언트 오류500: Internal Server Error서버 오류REST APIHTTP 통신으로 동작하는 어플리케이션 기능을 정의하는 규칙REST API 핵심URL을 이용한 자원의 표현HTTP 메서드를 이용한 행위의 표현HATEOAS 준수⇒ 복잡한 비즈니스에서는 모든 기능을 REST를 준수하며 개발하기 어렵기 때문에 가능한 규칙을 따르되, 불가피한 부분은 팀의 컨벤션의 따라 개발클라이언트에서 서버로 데이터를 전달하는 방법Query ParameterHTTP Request BodyPath Variable4. 데이터베이스란DBMS(DataBase Management System)데이터베이스를 저장하고 관리할 수 있는 응용 프로그램크게 관계형과 비관계형으로 나뉨관계형 데이터 베이스데이터를 행과 열로 이루어진 표의 형태로 저장가장 널리 쓰이는 데이터 베이스테이블의 속성 = 컬럼한 열의 데이터 = 레코드유일한 레코드를 식별할 수 있는 컬럼의 최소 조합을 후보키 라고 함후보키 중 대표로 지정한 것은 기본 키 라고 함두 테이블에서 하나의 레코드에 다른 테이블의 여러 레코드가 관계가 있으면 1:N 관계라 함필요에 따라 N:M 관계가 될 수 도있는데 이럴땐 중간에 맵핑 테이블로 관계를 풀어서 사용관계형 DBMS 종류Oracle DatabaseMySQLPostgreSQL비관계형 DBMS 종류MongoDBRedis5. JPA란?ORM객체지향 프로그래밍의 인스턴스와 관계형 데이터베이스를 매핑해주는 기술을 의미[장점]개발자가 직접 쿼리를 작성하는 과정이 생략되어 생산성이 향상됨데이터를 객체지향적 관점으로 접근 가능구체적인 DBMS에 대한 의존성이 낮아짐[단점]데이터를 정확히 다루기 위해 충분한 학습이 필요ORM만으로 원하는 모든 기능을 구현하기에는 한계가 있음트랜잭션여러 개의 데이터베이스 작업을 하나로 묶어주는 논리적인 단위[대표적인 예시]두 계좌가 있고 이체를 시키는 경우 두 계좌 중 하나만 실행되면 안됨. 그래서 두 작업을 트랜잭션으로 묶어 모두 성공하고너 모두 실패하도록 보장해주는 것이 트랜잭션임커밋: 트랜잭션으로 묶인 모든 작업을 데이터베이스에 영구히 반영하는 작업롤백: 트랜잭션으로 묶인 모든 작업을 원상복구하는 작업영속성 컨텍스트어플리케이션의 로직과 데이터베이스 사이에 있는 임시 메모리JPA는 DB에 접근 전 영속성 컨텍스트를 거침[특징]1차 캐시: 조회한 데이터의 임시 저장소, SELECT문 수행 전 1차 캐시에 데이터가 있으면 쿼리를 수행하지 않음더티 체킹: 영속성 컨텍스트 내의 데이터에 변화가 있을 경우 자동으로 UPDATE문을 수행쓰기 지연: INSERT, UPDATE를 즉시 수행하지 않고 영속성 컨텍스트 내에 모아뒀다가 트랜잭션이 종료될 때 한 번에 수행[실습]이 부분은 실습 진행하면서 몰랐던 부분들 기록하는 정도개발 환경 구성평소에는 Oracle의 JDK를 주로 사용했었는데 해당 강의는 Azul JDK를 사용해서 진행한다. 기존에 있는 Oracle의 java 17과 충돌이 있진 않을까 걱정했는데 Zulu JDK도 잘 돌아가서 강의대로 진행하면 될 것 같다.Git과 GitHub.gitignore 설정손쉽게 gitignore파일 생성해주는 사이트 📎https://www.toptal.com/developers/gitignore이 사이트의 존재는 알고있었는데 사용법을 찾아보기 귀찮아서 그냥 다른 프로젝트에서 사용했던 ignore파일 복사해오거나 chatgpt한테 시켜서 ignore파일을 생성해왔다근데 강의에서 사용하는걸 보니 그냥 이거 쓰는게 더 편할 것 같다해당 사이트에 들어가 현재 프로젝트의 개발 환경만 키워드로 작성해주면 됨ex) Windows, Intellij+all, Kotlin, Gradle, Git생성된 ignore 내용을 현재 프로젝트에 있는 .gitignore 파일 하단에 붙여넣기깃 몇 번 쓰다보면 ignore파일이 얼마나 중요한지 알게됨.. 초반에 귀찮다고 설정 안해놓으면 오만가지 파일이 다 깃에 올라가고 깃이 드러워져서 보기싫어짐..(이젠 꼬박꼬박 ignore 생성해서 추가해야지)프로젝트 환경 변수 설정datasource와 jpa 셋팅 과정 시 상수로 값을 넣어놔야하는데이걸 자바 코드에 직접 넣게 되면같은 값을 여러 곳에서 사용 시 변경될 때마다 모든 위치의 값을 바꿔줘야함개발 서버와 운영 서버에서 다른 상수 값을 쓰고 싶을때 빌드할 때마다 값 변경해줘야함 💡환경변수: 환경 마다 바뀌는 값⇒ 번거로움.스프링 Profile과 application.yml스프링에서 애플리케이션의 Profile을 정의할 수 있는 기능을 제공함실행 시 환경 변수로 정의 가능따로 정의되지 않고 실행되면 default로 설정됨로그에 어떤 profile로 셋팅이 되었는지 나옴application-{Profile}형식으로 yml 또는 properties파일을 생성하면 Profile에 따라 상수 값 설정 가능소스 코드에서는 enviornment를 이용해 키로 값을 가져올 수 있음[파일 위치]src/resources에 기본적으로 application.properties 파일이 생성됨현재 강의에서는 두개의 셋팅 값 사용application-default.yml(개발용)application-docker.yml(배포용)yaml파일 설명spring:이 안에 들어있는 값들은 spring에 대한 설정 값jpa:이 안에 들어있는 값들은 jpa에 대한 설정 값show-sql: 로그에 sql문 보여줌ddl-auto: create로 해놓으면 프로젝트 실행 시 모든 테이블을 create함(=즉 Run할 때마다 날라감). 초기 생성 외 평상시에는 none으로 놓으면 됨format_sql: 값이 true면 로그에 찍히는 sql문 줄 맞춰서 출력datasource:사용할 DB 정보(DB url, 계정, 비번 등..)h2:h2사용시엔 h2콘솔을 사용해야 해서 해당 값들 설정✅미션미션을 진행할 프로젝트 주제는 간단한 커뮤니티 페이지를 만들 생각이다.아직 미션을 수행하진 않아서 회고를 적을 건 없지만 이번주 미션은 깃 레포지토리 생성과 테이블 설계가 다여서 많은 시간이 소요될 것 같지는 않다.대충은 테이블을 어떻게 생성할지 구상만 해놓은 상태..📅1주차 회고이번주는 이론 관련 강의가 주 였다. 아예 모르던 내용들은 아니라서 간단한 개념 정리 느낌으로 수강하였다. 용어들이 익숙은 하지만 아직 정확하게 숙지된건 아니라 틈틈히 복습하면서 익히는게 좋을 것 같다는 생각이 들었다.실습은 아직 엔티티 생성까지만 다뤘는데, 자바가 아닌 코틀린을 사용하다보니 아직은 어색한 부분들이 있는 것 같다(문법 적으로). 아직까지는 강의를 보면서 따라 치는 정도인데 이번 강의를 들으면서 코틀린 기본 문법들을 익혀야겠다는 생각이 들었다.
백엔드




