블로그

제 생각이예요

인프런 블로그가 활성화 되려면   1. 카테고리가 있어야 한다.   2. 코드 복붙할때 깔끔하게 되어야 한다. (벨로그 처럼 깔끔하게 코드를 끍어 오면 좋겠어요)      코드블럭 편집기를 따로 만들어 주세요(T스토리 보다 더 깔끔한 코드블럭편집기를 만들어주세요!!)      코드만 잘 끌와도 정리하는데,  최소 1시간은 save할수 있습니다!!   3. 강의노트에 적은거 블로그로 부드럽게 끌고 와야된다.   3-1. 강의노트 개선부탁드릴께요         마크다운 형식이라서 정리하기가 너무 어려워요.        숫자는 한번 적고 더이상 그만 적고 싶은데  1.. 2.. 3..4         가끔은 코드를 강의 노트에 타이핑하는 경우가 있는데 그러면        코드 와꾸틀이 와르르 무너져요. 띄어쓰기가 허용이 안되고 다 붙어 버려요.   4. 카테고리 예시 HTML/CSS 자바   자바의 정석   생활코딩(자바) 스프링    스프링의 핵심원리   스프링의 기본편   스프링 MVC패턴 1편   스프링 MVC패턴 2편 쿠버네티스   그림으로 배우는 쿠버네티스 알고리즘   파이썬 알고리즘   자바 알고리즘    위의 카테고리 예시로 보면 큰 카테고리 : HTML/CSS                          자바                         스프링                         쿠버네티스                         알고리즘 위와 같이 큰 카테고리가 만들어진 후 에  스프링을 클릭하면 하위 카테고리    스프링 핵심원리                              스프링 기본편                              스프링 MVC패턴 1편                              스프링 MVC패턴 2편   이렇게 만들어진 후에  스프링 핵심원리 클릭하면 스프링 핵심원리 정리한 목록 뜨면서 스크롤을 내리면서 글을 볼 수 있었으면 좋겠습니다.   5. 늘리거나 줄이거나 구부러뜨리거나 휘게 할 수 있는 화살표 같은 거 넣는 기능 만들어 주시면 정말 좋겠써요.       제가 정리하면서 느끼는 건데,      A함수에서 B함수를 호출하고, 매개변수를 전달하는 것      이런걸 표시 하고 싶은데,      통상적으로 코드를 정리한 다음 그 코드를 스크린샷으로 찍어서      그림판에서 그 스크린샷 위에 화살표를 그려서 덧빵을 해요.      그러면 다른 이름으로 저장하고, 또 새로운 편집기에서 다시 불러와서 편집을 해요.       작업속도가 너무 더뎌요.         intellij나 이클립스에서 작성한 코드를 그대로 끌고 와서       웹 상의 편집기에서 긁어온 코드 위 화살표 같은 거 자유자재로 그렸으면 좋겠습니다.   인프런에는 워낙 출중한 프론트엔드 프로그래머 분들이 많으시니까 이런거 만드는 거는  반나절만에 다 만드실꺼예요                                                            

블로그t스토리코드블럭spring화살표

저니

스프링 가이드 목록 2022

* page : https://spring.io/guides     클릭하시면 해당 페이지로 넘어갑니다. 🍃 Building a RESTful Web Service Scheduling Tasks Consuming a RESTful Web Service Building Java Projects with Gradle Building Java Projects with Maven Accessing Relational Data using JDBC with Spring Uploading Files Authenticating a User with LDAP Messaging with Redis Messaging with RabbitMQ Accessing Data with Neo4j Validating Form Input Building a RESTful Web Service with Spring Boot Actuator Messaging with JMS Creating a Batch Service Securing a Web Application Building a Hypermedia-Driven RESTful Web Service Accessing Data in Pivotal GemFire Integrating Data Caching Data with Pivotal GemFire Managing Transactions Accessing Data with JPA Accessing Data with MongoDB Serving Web Content with Spring MVC Converting a Spring Boot JAR Application to a WAR Creating Asynchronous Methods Handling Form Submission Building an Application with Spring Boot Using WebSocket to build an interactive web application Working a Getting Started guide with STS Consuming a RESTful Web Service with AngularJS Consuming a RESTful Web Service with jQuery Enabling Cross Origin Requests for a RESTful Web Service Consuming a SOAP web service Accessing JPA Data with REST Accessing Neo4j Data with REST Accessing MongoDB Data with REST Accessing Data in Pivotal GemFire with REST Producing a SOAP web service Caching Data with Spring Deploying to Cloud Foundry from STS Spring Boot with Docker Working a Getting Started guide with IntelliJ IDEA Creating CRUD UI with Vaadin Service Registration and Discovery Centralized Configuration Testing the Web Layer Accessing data with MySQL Creating a Multi Module Project Creating API Documentation with Restdocs Messaging with Google Cloud Pub/Sub Building a Reactive RESTful Web Service Consumer Driven Contracts Accessing Vault Vault Configuration Accessing Data Reactively with Redis Deploying a Spring Boot app to Azure Building a Gateway Client-Side Load-Balancing with Spring Cloud LoadBalancer Spring Cloud Stream Spring Cloud Data Flow Spring Cloud Task Spring Boot Kubernetes Accessing data with R2DBC Spring Cloud Circuit Breaker Guide Observability with Spring Building a Guide with VS Code Accessing Data with Cassandra Spring Security Architecture Spring Boot Docker Spring on Kubernetes Building REST services with Spring Spring Security and Angular React.js and Spring Data REST Spring Boot and OAuth2 Building web applications with Spring Boot and Kotlin Spring Boot with Kotlin Coroutines and RSocket Metrics and Tracing with Spring     var list = Array.from(document.getElementsByClassName('guide-link')); list.forEach((e) => { console.log('* [' + e.innerHTML + '](http://spring.io' + e.getAttribute('href') + ')'); }); 출처 : https://okky.kr/article/1209730

백엔드스프링프레임워크가이드springframeworkbackendguides

[냥이와봄] 12주차 (21.09.27 ~)

✍ 11주차 돌아보기 월: 추석 화: 추석 수: 추석 목: 전원완료 금: 전원완료 토: 옐로우티거님 제외(사유: 본가)            🙆‍♀️ 스터디 멤버 (총 8명) 냥집사, 집가고싶다, 이대건, 커피볶는정콩, 열공, boss, 옐로우티거, 거니       😨 경고현황 옐로우티거님 1회 경고 이대건님 1회 경고       💦 특이사항 1. 스프링 스터디로 시작하였지만 오프라인 및 인원증가로 인하여 모각공(모두 각자 공부)의 스터디로 변함.     📖 12주차 커리큘럼 ( 스프링 핵심 원리 - 기본편, 스프링 MVC1편 - 백엔드 웹 개발 핵심 기술 ) 총 9강 월 : 섹션5. 스프링 MVC - 컨트롤러 통합 화 : 섹션5. 스프링 MVC - 실용적인 방식 수 : 섹션5. 정리 목 : 섹션6. 프로젝트 생성 금 : 섹션6. 로깅 간단히 알아보기 토 : 섹션6. 요청 매핑       섹션6. 요청 매핑 - API 예시       섹션6. HTTP 요청 - 기본, 헤더 조회       섹션6. HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form       📚 스터디원들 진행 사항 냥집사 : HTML, 자바의 정석 동강 10장 수강중 이대건 : 오브젝트 책 정독 거니 : 모든 개발자를 위한 HTTP 웹 기본 지식 - 김영한님 강의 수강 boss : 스프링 부트 개념과 활용 - 백기선님 강의 수강 집가고싶다 : HTTP웹 기본 지식, 자바의 정석, 정보처리기사 실기 준비 열공 : 자바의 정석 동강 수강 커피볶는정콩 : 스프링 커리큘럼 그대로 진행 옐로우티거: ...?     * 옐로우티거님은 댓글로 진행사항 작성해주세요.

웹 개발spring모각공

대협

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

[인프런 워밍업 클럽 2기 - BE] 3주차 발자국이 블로그는 정보근님의 입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 강의 기반으로 코드작성과 코드설명을 적었습니다1. controller test 코드 분석애너테이션 조사@AutoConfigureMockMvcMockMvc를 자동으로 설정해 주는 애너테이션. 이 애너테이션을 통해 HTTP 요청을 수행하고 응답을 확인할 수 있다.MockMvc 란 ?실제로 서버를 띄우지 않고 컨트롤러를 테스트할 수 있는 도구@DisplayName("Test")테스트 클래스를 시작할 때 테스트 클래스의 이름을 지정해서 테스트 리포트에 표시된다.위 코드를 실행시키면 리포트에 TEST 라는 이름으로 테스트 성공유무가 표시됨.@Configuration : Spring에서 Bean을 수동으로 등록하기 위해서 사용메서드 분석 @Test @DisplayName("Introductions 조회") fun testGetIntroductions() { val uri = "/api/v1/introductions" val mvcResult = performGet(uri) val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8) val jsonArray = JSONArray(contentAsString) assertThat(jsonArray.length()).isPositive() }api/vi/introduction을 uri 변수에 넣음.HTTP GET 요청을 보낸후, 응답을 문자열로 변환한 후에 JSONArray로 변환JSONArray의 길이가 0보다 큰지 검증, 응답 데이터가 비어 있지 않음을 확인 @Test @DisplayName("Link 조회") fun testGetLinks() { val uri = "/api/v1/links" val mvcResult = performGet(uri) val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8) val jsonArray = JSONArray(contentAsString) assertThat(jsonArray.length()).isPositive() } api/vi/links을 uri 변수에 넣음HTTP GET 요청을 보낸후, 응답을 JSONArray로 변환배열의 크기와, 그 데이터가 존재하는지 검증@Test @DisplayName("Resume 조회") fun testGetResume() { val uri = "/api/v1/resume" val mvcResult = performGet(uri) val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8) val jsonObject = JSONObject(contentAsString) assertThat(jsonObject.optJSONArray("experiences").length()).isPositive() assertThat(jsonObject.optJSONArray("achievements").length()).isPositive() assertThat(jsonObject.optJSONArray("skills").length()).isPositive() } /api/vi/resume를 uri변수에 넣음GET요청을 보낸후, 응답을 JSONObject로 변환experiences, achievements,skills를 JSONArray로 변환 후 그 값이 양수인지와 데이터가 존재하는지 검증@Test @DisplayName("Projects 조회") fun testProjects() { val uri = "/api/v1/projects" val mvcResult = performGet(uri) val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8) val jsonArray = JSONArray(contentAsString) assertThat(jsonArray.length()).isPositive() } /api/vi/projects를 uri 변수에 넣음GET요청을 보낸후 응답 본문을 JSONArray로 변환하고, 배열의 길이가 양수인지와 데이터가 존재하는지 검증private fun performGet(uri: String): MvcResult { return mockMvc .perform(MockMvcRequestBuilders.get(uri)) .andDo(MockMvcResultHandlers.print()) .andReturn() } perform(MockMvcRequestBuilders.get(uri)) : 특정 uri에 GET 요청을 보내는 코드. 서버를 띄우지 않고 API 엔드포인트의 동작을 확인할 수 있음andDo(MockMvcResultHandlers.print()) : 요청과 응답을 콘솔에 출력andReturn() : MvcResult를 반환2. 부분 코드 분석<html *lang*="ko" *xmlns:th*="<http://www.thymeleaf.org>" *th:replace*="~{presentation/layouts/layout-main :: layout(~{::#content})}">Thymeleaf 템플릿 엔진에서 레이아웃을 정의하고 해당 레이아웃을 재사용하는 방식, 템플릿 코드의 중복을 줄이고, HTML 파일 간에 공통 요소를 재사용할 수 있게 하는 것. th:replace="~{presentation/layouts/layout-main :: layout(~{::#content})}"th:replace : 다른 파일을 가져와 현재 위치에 삽입하는 기능 수행~{presentation/layouts/layout-main :: layout(~{::#content})} :~{presentation/layouts/layout-main} : layout-main.html 파일 참조:: layout : layout 이라는 이름의 fragment를 사용하겠다는 의미, 위 코드에서는 layout fragment를 가져오겠다는 뜻(~{::#content}) : id="content" 로 지정된 부분을 layout fragment의 특정 위치에 대체할 것이라는 의미3. interceptor 코드 분석@Component class PresentationInterceptor( private val httpInterfaceRepository: HttpInterfaceRepository ) : HandlerInterceptor { override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) { val httpInterface = HttpInterface(request) httpInterfaceRepository.save(httpInterface) } }private val httpInterfaceRepository: HttpInterfaceRepository의존성 주입을 통해서 Repository를 사용할 수 있다.override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) {afterCompletion : HTTP 요청 처리 후에 호출되는 메서드val httpInterface = HttpInterface(request)httpInterfaceRepository.save(httpInterface)request 정보를 담고 있는 httpInterface 객체를 생성 후, httpInterfaceRepository에 저장. @Configuration class PresentationInterceptorConfiguration( private val presentationInterceptor: PresentationInterceptor ) : WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(presentationInterceptor) .addPathPatterns("/**") .excludePathPatterns("/assets/**", "/css/**", "/js/**", "/admin/**", "h2**", "/favicon.ico", "/error") } }addInterceptors인터셉터를 등록하는 메서드registry.addInterceptor(presentationInterceptor)addInterceptor 메서드를 통해 presentationInterceptor를 등록addPathPatterns("/**")모든 요청 경로에 대해 인터셉터 적용excludePathPatterns(...) : 인터셉터가 동작하지 않도록 설정/assets/**: 정적 자원(예: 이미지, 폰트 등)./css/**: CSS 파일./js/**: JavaScript 파일./admin/**: 관리 페이지.h2**: H2 데이터베이스 콘솔./favicon.ico: 사이트 아이콘./error: 오류 처리 경로.[미션4] 조회 REST API 만들기-회고이번 미션을 하면서 @GetMapping 어노테이션에 대해서는 어느정도 이해를 할 수 있었다. 저번 발자국에서 @Id, @GeneratedValue 어노테이션에 대해서 적었지만 아직 부족하다는 걸 알 수 있었고, @ManyToOne 어노테이션에 대해 더 깊이 공부해야겠다는걸 깨달았다. 아직 Spring에 본질적인걸 이해를 못한걸수도 있는거 같다. 코드를 작성하면 할수록 더 깊게 공부를 해야했고, JAVA 문법도 다시 해야겠다는걸 뼈저리게 느끼면서 한것 같다...3주차 회고Spring은 하면 할수록 재밌는건 맞다. Test 코드를 작성할 때도 이래서 이코드가 작동이 되는것도 알 수 있고, 부분적인 걸 따로 모듈화? 해서 만드는것도 재밌다. 이번 워밍업 클럽도 곧 종료가 되는데 java의 중요성도 깨달아서 java공부도 열심히 하고, Spring에 대해서 더 깊게 공부할거다!참고로 중간점검 때 정보근님께서 내가 작성한 질문에 대해 답변을 해주신거 같다. 나의 질문은 이번 워밍업 클럽이 종료가 되면 java의 정석, spring공부를 할건데 추천해줄 강의가 있냐 라는 질문이었다. 답변은 역시 김영한님의 강의였고 다른 강의도 추천해주셨는데 이거는 확인해보고 글을 수정해야겠다...! 무튼, spring을 선택한건 잘한 일 같다...!

백엔드워밍업클럽javakotlinspringTest코드미션4회고록코드설명백엔드

[냥이와봄] 16주차 (21.10.24 ~ )

✍ 15주차 돌아보기 (제외 목록만 작성) 월: - 화: boss님(사유: 약속) 수: - 목: 열공님(사유: 약속), 옐로우티거님(사유: 회식) 금: 열공님(사유: 백신예정), 거니님(사유: 약속), 집가고싶다님(사유: 약속), 옐로우티거님(사유: 잠수) 토: 진행중            🙆‍♀️ 스터디 멤버 (총 8명) 냥집사, 집가고싶다, 이대건, 커피볶는정콩, 열공, boss, 옐로우티거, 거니       😨 경고현황 옐로우티거님 4회 경고 (10/22) (냥집사가 제주도에 힐링와서 강퇴 보류중...) 이대건님 1회 경고 집가고싶다님 1회 경고 열공님 1회 경고 (10/20) ------------------------------------ 10월 이전 경고 초기화 현황 이대건님 0회 경고 옐로우티거님 2회 경고 (10월 경고) 집가고싶다님 1회 경고 (10월 경고) 열공님 1회 경고 (10월 경고)       💦 특이사항 1. 스프링 스터디로 시작하였지만 오프라인 및 인원증가로 인하여 모각공(모두 각자 공부)의 스터디로 변함. 2. "11시 이후 인증 호출시 경고 1회 부여" 투표를 하였지만 아직 추가하지 않은 상황. 다시 스터디원과 조율 후 추가 예정 3. 15주차 기념 스터디에 바라는 점 투표 진행(10.18~10.22) 결과로 10월 20일에 디스코드 서버 오픈 (참여는 자유) 4. 3개월마다 경고 누적 횟수 초기화 룰 투표 진행중(10.23~10.24) - 투표결과 규칙 추가와 함께 10월 이전의 경고는 다 초기화.     📖 16주차 커리큘럼 ( 스프링 MVC2편 - 백엔드 웹 개발 활용기술 ) 총 9강 월 : 섹션1. 프로젝트 생성 화 : 섹션1. 타임리프 소개 수 : 섹션1. 텍스트 - text, utext 목 : 섹션1. 변수 - SpringEL 금 : 섹션1. 기본 객체들 토 : 섹션1. 유틸리티 객체와 날짜       섹션1. URL 링크       섹션1. 리터럴       섹션1. 연산

웹 개발spring

[냥이와봄] 14주차(21.10.11 ~)

✍ 13주차 돌아보기 월: 전원완료 화: 열공님 제외(사유: 건강) 수: 열공님 제외(사유: 건강) 목: 전원완료 금: 전원완료 토: boss님 제외(사유: 운동시합)            🙆‍♀️ 스터디 멤버 (총 8명) 냥집사, 집가고싶다, 이대건, 커피볶는정콩, 열공, boss, 옐로우티거, 거니       😨 경고현황 옐로우티거님 1회 경고 이대건님 1회 경고       💦 특이사항 1. 스프링 스터디로 시작하였지만 오프라인 및 인원증가로 인하여 모각공(모두 각자 공부)의 스터디로 변함. 2. "11시 이후 인증 호출시 경고 1회 부여" 투표를 하였지만 아직 추가하지 않은 상황. 다시 스터디원과 조율 후 추가 예정     📖 14주차 커리큘럼 ( 스프링 핵심 원리 - 기본편, 스프링 MVC1편 - 백엔드 웹 개발 핵심 기술 ) 총 9강 월 : 섹션7. 프로젝트 생성 화 : 섹션7. 요구사항 분석 수 : 섹션7. 상품 도메인 개발 목 : 섹션7. 상품 서비스 HTML 금 : 섹션7. 상품 목록 - 타임리프 토 : 섹션7. 상품 상세       섹션7. 상품 등록 폼       섹션7. 상품 등록 처리 - @ModelAttribute       섹션7. 상품 수정       📚 스터디원들 진행 사항 냥집사 : 자바의 정석 동강 12장 수강중 이대건 : 오브젝트 책 정독 거니 : 모든 개발자를 위한 HTTP 웹 기본 지식 - 김영한님 강의 수강 boss : 스프링 부트 개념과 활용 - 백기선님 강의 수강 집가고싶다 : HTTP웹 기본 지식, 자바의 정석, 정보처리기사 실기 준비 열공 : 자바의 정석 동강 수강 커피볶는정콩 : 스프링 커리큘럼 그대로 진행 옐로우티거: ...?     * 옐로우티거님은 댓글로 진행사항 작성해주세요.

웹 개발spring

[냥이와봄] 13주차 (21.10.04 ~)

✍ 12주차 돌아보기 월: 전원완료 화: 냥집사 제외(사유: 백신) 수: 전원완료 목: 전원완료 금: 열공님 제외(사유: 친구와 약속) 토: 전원완료            🙆‍♀️ 스터디 멤버 (총 8명) 냥집사, 집가고싶다, 이대건, 커피볶는정콩, 열공, boss, 옐로우티거, 거니       😨 경고현황 옐로우티거님 1회 경고 이대건님 1회 경고       💦 특이사항 1. 스프링 스터디로 시작하였지만 오프라인 및 인원증가로 인하여 모각공(모두 각자 공부)의 스터디로 변함.     📖 13주차 커리큘럼 ( 스프링 핵심 원리 - 기본편, 스프링 MVC1편 - 백엔드 웹 개발 핵심 기술 ) 총 9강 월 : 섹션6. HTTP 요청 파라미터 - @RequestParam 화 : 섹션6. HTTP 요청 파라미터 - @ModelAttribute 수 : 섹션6. HTTP 요청 파라미터 - 단순텍스트 목 : 섹션6. HTTP 요청 파라미터 - JSON 금 : 섹션6. 응답 - 정적 리소스, 뷰 템플릿 토 : 섹션6. HTTP 응답 = HTTP API, 메세지 바디에 직접 입력       섹션6. HTTP 메세지 컨버터       섹션6. 요청 매핑 핸들러 어댑터 구조       섹션6. 정리       📚 스터디원들 진행 사항 냥집사 : HTML, 자바의 정석 동강 10장 수강중 이대건 : 오브젝트 책 정독 거니 : 모든 개발자를 위한 HTTP 웹 기본 지식 - 김영한님 강의 수강 boss : 스프링 부트 개념과 활용 - 백기선님 강의 수강 집가고싶다 : HTTP웹 기본 지식, 자바의 정석, 정보처리기사 실기 준비 열공 : 자바의 정석 동강 수강 커피볶는정콩 : 스프링 커리큘럼 그대로 진행 옐로우티거: ...?     * 옐로우티거님은 댓글로 진행사항 작성해주세요.

웹 개발spring모각공

[냥이와봄] 10주차 (21.09.13 ~)

✍ 9주차 돌아보기 월: 전원완료 화: 전원완료 수: 전원완료 목: 전원완료 금: 전원완료 토: 이대건님 제외(사유: 코테), boss님 제외(사유: ??), 집가고싶다님 제외(사유: 집안사정)            🙆‍♀️ 스터디 멤버 (총 9명) 냥집사, 집가고싶다, kkm, 이대건, 커피볶는정콩, 열공, boss, 옐로우티거, 거니       😨 경고현황 옐로우티거님 1회 경고 이대건님 1회 경고 kkm님 1회 경고    💦 특이사항 1. 스터디원 증가 2. 비대면 온라인 스터디 3. 모각공(모두 각자 공부) 라는 스터디라는 점에서 예상보다 훨씬 심하게 개인 공부 스터디로 바뀌어 버렸다. 누구는 spring, 누구는 자바, 누구는 다른 강의의 spring  등등 너무 달라져서 각자 무슨 공부를 하는지 정리가 필요하다. [2회 이상 인증 호출시(ex. **님 공부하셨나요?) 1회 경고 추가. 단, 미리 사유를 냈을 경우 무효.]라는 새로운 규칙을 스터디원끼리 의논하여 과반수 이상 찬성할 경우 추가할 예정이다. 규칙을 만드는 이유는 공부를 했을 경우 스터디에 인증하는것은 단 1초면 가능한 일이기에 이 1초도 안하거나 못한다는건 스터디를 할 마음이 없다고 생각되기 때문이다.   📖 10주차 커리큘럼 ( 스프링 핵심 원리 - 기본편, 스프링 MVC1편 - 백엔드 웹 개발 핵심 기술 ) 총 10강 월 : 섹션3. MVC 패턴 - 개요 화 : 섹션2. MVC 패턴 - 적용 수 : 섹션3. MVC 패턴 - 한계, 섹션3. 정리 ( 총 2강 - 9분 ) 목 : 섹션4. 프론트 컨트롤러 패턴 소개 금 : 섹션4. 프론트 컨트롤러 도입 - v1 토 : 섹션4. View 분리 - v2       섹션4. Model 추가 - v3       섹션4. 단순하고 실용적인 컨트롤러 - v4       섹션4. 유연한 컨트롤러1 - v5    

웹 개발spring

V_브이_v

[인프런 워밍업 클럽 스터디 2기] : 웹 개발의 첫 발자국

안녕하세요!제 첫 블로그에서는 자바 스프링(Spring Framework)을 처음 배우면서 느꼈던 점과1주차에 배운 주요 개념들을 간단하게 정리해보려고 합니다.새로운 것을 배운다는 생각에 약간 신나기도 합니다 ㅎ이번 스프링 스터디를 통해 스프링을 배우며 얻은 지식을 꾸준히 정리해 블로그에 올리는 습관을 만들어보겠습니다. 간단한 웹 이론웹 서비스는 크게 세 가지 요소로 구성됩니다: 클라이언트, 서버, 데이터베이스.- 클라이언트: 요청을 보내는 주체로, 컴퓨터나 스마트폰의 브라우저 등 사용자가 접근하는 환경입니다.- 서버: 클라이언트의 요청을 받아 작업을 수행하는 주체입니다.- 데이터베이스(DB): 데이터의 집합으로, DBMS를 통해 데이터를 관리합니다.  웹 프레임워크와 라이브러리의 차이웹 프레임워크: 웹 개발을 편리하게 도와주는 도구로, 이미 정해진 틀이 있습니다.예를 들어 이케아의 가구처럼 정해진 틀과 구성이 있다고 생각할 수 있습니다.라이브러리: 정해진 틀이 없는 도구로, 필요에 따라 사용할 수 있는 철물점의 도구와 비슷합니다. 스프링 프레임워크스프링은 자바로 만들어진 웹 프레임워크로, 다양한 주요 개념이 있습니다.MVC 패턴MVC 패턴은 모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 소프트웨어 아키텍처 디자인 패턴입니다.- 모델: 데이터를 담는 역할을 합니다.- 컨트롤러: 클라이언트의 요청을 받아 작업을 수행하고, 결과 데이터를 모델에 담습니다.- 뷰: 사용자에게 보여지는 화면으로, 모델에서 데이터를 가져와 표시합니다. 레이어드 아키텍처- 컨트롤러: 클라이언트의 요청을 받는 인터페이스로, 데이터를 검증하고 서비스의 메소드를 호출합니다.- 서비스: 데이터 처리 로직을 수행하고, 저장소(DB)에 대한 작업을 요청합니다.- 저장소: DB에 접근하여 데이터를 처리하며, 여러 서비스에서 공통적으로 사용할 수 있는 처리 방법을 제공합니다. 스프링 빈(Spring Bean)스프링에서 관리하는 자바 객체를 빈(Bean)이라고 합니다. 스프링 빈은 스프링 컨테이너에서 생성되고 관리되며, 다음과 같은 특징이 있습니다.- 관리되는 객체: 스프링 컨테이너에서 생성되고 관리됩니다.- 재사용 가능: 애플리케이션 전체에서 재사용이 가능합니다.- 의존성 주입: 스프링이 빈들 간의 관계를 자동으로 설정해줍니다. 스프링 부트에서의 의존성 주입- 생성자 주입: 클래스 생성자를 사용하는 방식으로 가장 안전합니다.- 수정자 주입: @Autowired 같은 어노테이션을 사용하여 필드를 주입하는 방식입니다.- 필드 주입: 수정자 주입과 비슷하게 동작하지만, 의존성을 바꿀 때 문제가 생길 수 있어 신중히 사용해야 합니다. HTTP와 REST APIHTTP 메소드- GET: 리소스를 가져오는 요청- POST: 리소스를 생성하는 요청- PUT/PATCH: 리소스를 업데이트하는 요청- DELETE: 리소스를 삭제하는 요청- 오류 코드: 400번대는 클라이언트의 문제, 500번대는 서버의 문제를 나타냅니다.REST API: HTTP 통신을 통해 애플리케이션 기능을 정의하는 규칙입니다.URL을 자원으로 활용하고, HTTP 메소드를 이용해 행위를 표현합니다. JPA (Java Persistence API)JPA는 자바 객체를 DB의 테이블로 매핑해주는 기술입니다. 이를 통해 개발자는 직접 쿼리를 작성하지 않고도 객체지향적인 접근이 가능합니다.- 장점: 생산성 증가, 객체지향적 접근, DBMS 의존성 감소- 단점: 충분한 학습이 필요하고, 복잡한 쿼리를 처리하는 데 한계가 있을 수 있습니다. 느낀 점과 회고스프링을 처음 배우며 새로운 개념들을 많이 접했습니다.특히 빈과 의존성 주입 개념이 이해되면서도 어려우면서도 아리까리?합니다.직접 프로젝트에 적용해보면서 개념을 익히는 것이 가장 좋겠다고 생각했습니다.기본적인 웹 이론에 대해 잘 모르고 있던 부분이 많아 부끄럽기도 했지만, 이번 기회를 통해 기초부터 다시 다질 수 있어서 좋았습니다.앞으로도 더 많은 기능을 학습하고 블로그에 정리하며 성장해 나가고자 합니다.오늘 남은 시간을 활용해서 섹션 3의 진도를 나가고 최종장에는 개인프로젝트를 실제로 동작하게끔 만들고 싶습니다!!

백엔드javaspring스터디미션발자국백엔드

최강현

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

Day 7. 스프링 컨테이너의 의미와 사용 방법 이 날 강의를 통해서 드디어 베일에 쌓인 의존성을 공부하는 기회가 되었다.의존성이란다음 코드를 보면 UserController의 생성자는 JdbcTemplate이라는 클래스를 필요로 하고 있다. @RestController public class UserController { private final UserService userService; public UserController(JdbcTemplate jdbcTemplate) { this.userService = new UserService(jdbcTemplate); } }이것을 어려운 말로 '의존한다'라고 한다. 스프링 빈이란스프링부트 서버를 시작할 때 자동으로 해주는 것 중 하나가 거대한 컨테이너(실제 컨테이너를 떠올리면 된다)를 만드는 것이다.이 컨테이너 안에는 클래스가 들어가게 되는데 이렇게 들어간 클래스를 스프링 빈이라고 부른다.클래스가 들어갈 때는 이 빈을 식별 할 수 있는 이름 및 타입과 다양한 정보가 들어가고 인스턴스화도 이루어진다. Day8. Spring Data JPA를 사용한 데이터베이스 조작 8일차 강의를 통해 SQL을 통해서 접근했던 DB를 자바 객체를 DB와 매핑 시켜서 사용하는 JPA를 학습하였다. JPA란객체와 관계형 데이터베이스의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙JPA는 API이기 때문에 말 그대로 규칙이고 (자바의 인터페이스와 같다) 이 규칙을 누군가는 실제로 구현을 해야한다그 구현체가 바로 Hibernate이다. Day9. 트랜잭션과 영속성 컨텍스트 9일차 강의는 서비스 계층의 남은 중요한 역할인 트랜잭션에 대해 배웠다.트랜잭션이란 쪼갤 수 없는 업무의 최소단위이다.실제로 예를 들면 인터넷 결제를 진행할 때 1. 주문 기록을 저장하고 2. 포인트를 적립해주고 3. 구매 기록을 저장해준다이 3가지의 과정을 쪼갤 수 없는 하나의 최소단위로 묶고 하나가 실패하면 다른 모든 과정도 실패해버리게 만드는게 트랜잭션이다. Start transaction -> 성공하면 COMMIT -> 실패하면 ROLLBACK 또한 영속성 컨텍스트에 관해서도 학습했는데 조금 어려워서 꼭 기억해야하는것스프링에서는 트랜잭션을 사용하면 영속성 컨택스트가 생기고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다. 영속성 컨텍스트의 특별한 능력변경 감지 : 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save를 해주지 않더라도 알아서 변경을 감지하여 저장할 수 있게 해준다.쓰기 지연 : 영속성 컨텍스트에 의해 트랜잭션이 Commit 되는 시점에 SQL을 모아서 한 번만 날리게 된다.1차 캐싱 : ID를 기준으로 Entity를 기억하는 기능이다. 조금 더 복잡한 기능을 API로 구성하기 10일차는 앞서 배운 것들로 JPA를 이용해서 책 생성, 대출, 반납 API를 개발해보았다. 과제 과제 4일차https://devnter.tistory.com/entry/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-1%EA%B8%B0BE-4%EC%9D%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9CqueryForObject() 에 대한 학습이 필요. 과제 5일차https://devnter.tistory.com/entry/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-1%EA%B8%B0BE-5%EC%9D%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C주어진 코드를 클린코드로 리팩토링하는 과제를 수행하였다.클래스를 나누기에는 너무 작은 기능이라 생각해서 읽기 좋은 코드로만 수정한다는 생각으로 수정했는데나중에 금요일에 코치님이 따로 열어주신 특강에서 테스트를 하기 위해 역할 별로 클래스를 쪼개고 테스트를 하는 강의를 진행 해 주셔서 새로운 내용에 대해 학습하는 기회가 되었다리팩토링 전에는 테스트를 작성하자! (Junit5, assertj)given / when / then 패턴전략 패턴 (= NumberGenerator의 인터페이스화 + 구현체 갈아끼우기)일급컬렉션MVC 패턴출처 https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90# 아쉬운점좀 더 추가적인 학습(모르는것에 대한 학습)을 하려고 했는데 나태해져서 진행하지 못했다. 다음 마지막 주차는 좀 더 내가 모르는 내용에 대해 민감하게 반응해서 학습 하거나 학습 하지 못한다면 따로 블로그나 노션에 기록해서 추후 공부할 수 있도록 하겠다. 

백엔드인프런워밍업클럽자바스프링javaspring

최강현

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

강의 수강,과제 수행하면서 생겼던 궁금증을 해결하는 과정을 위주로 블로그 글을 작성하려고 합니다. 강의 수강 1일차 - OT 인프런 워밍업 클럽 스터디 및 일정 소개 코치님과 구글 밋을 통해 온라인 OT를 진행하였습니다.간단한 스터디 과정 및 일정에 대해 소개 해줬고, 코치님이 이거까지 알아야하나? 라는 주제로 의견을 공유해주셨습니다. 2일차 - 서버 개발을 위한 환경 설정 및 네트워크 기초 (1강 ~ 5강)2일차에는 서버, 네트워크, HTTP, API를 배웠다.API가 무엇인지 잘 몰랐는데 Input을 주면 OutPut이 나오는 함수와 비슷하다는걸 알게 되었다. API가 무엇인지 와닿지 않았는데 감을 잡을 수 있었다3일차 - 첫 HTTP API 개발 (6강 ~ 10강)GET API와 POST API를 개발해봤다.GET 요청은 쿼리를 통해서 요청을 주고 POST 요청은 body를 통해 요청을 주는데@RequestParam을 통해 주어지는 쿼리를 함수 파라미터에 넣는다.파라미터 대신에 DTO 객체를 넣어서 쿼리를 받을 수 있다public class CalculatorAddRequest{ private final int number1; private final int number2; public CalculatorAddRequest(int number1, int number2) { this.number1 = number1; this.number2 = number2; } public int getNumber1() { return number1; } public int getNumber2() { return number2; } }  post 요청은 body로 들어오는 JSON을 DTO로 바꿔주기 위해 @RequestBody를 사용해야하는데@RequestBody를 사용하면 DTO 객체에 생성자를 만들지 않아도 괜찮은데 이 이유가 궁금했다public class CalculatorMultiplyRequest { private int number1; private int number2; public int getNUmber1(){ return number1 } public int getNumber2(){ return number2; } }다음과 같은 이유를 찾을 수 있었다.Spring의 동작 방식: @RequestBody 어노테이션이 적용된 메서드 파라미터에는 HTTP 요청의 본문을 기반으로 객체가 생성됩니다. Spring은 이를 위해 클래스의 기본 생성자를 호출하여 빈 객체를 생성하고, 요청의 본문에서 추출한 데이터를 객체에 설정합니다.4일차 - 기본적인 데이터베이스 사용법 (11강 ~ 13강) MySQL의 사용법을 배웠다DDL(데이터 정의어) - CREATE, ALTER, DROPDML(데이터 조작어) - INSERT, UPDATE, SELECT, DELETE이 2개를 사용해서 데이터를 DB에 생성, 조회, 갱신, 삭제하고5일차 - 데이터베이스를 사용해 만드는 API (14강 ~ 16강)이 강의를 통해 사람이 직접 DB에 접속해서 DB를 조작하는게 아닌 Spring 서버가 MySQL DB에 접근해서 DB를 조작하게 API를 만들었다. 6일차 - 클린코드의 개념과 첫 리팩토링(17강~ 18강) 이 강의를 통해 저번 강의를 통해 만든 컨트롤러 클래스를 3개의 클래스로 쪼개 보았다.Controller 클래스 - API의 진입 지점으로써 HTTP Body를 객체로 변환Service 클래스 - 분기처리 로직Repository 클래스 - SQL을 사용해 실제 DB와의 통신을 담당 컨트롤러 클래스 작성 시 JdbcTemplate 객체를 생성하는데 new 키워드를 사용하지 않고 생성했다이게 어떻게 가능한지 궁금해서 찾아보니스프링에서는 보통 dependency injection을 사용하여 객체를 주입합니다. 이 코드에서도 JdbcTemplate jdbcTemplate는 생성자를 통해 주입됩니다.생성자 public UserController(JdbcTemplate jdbcTemplate)에서 JdbcTemplate 객체가 주입됩니다. Spring이 UserController를 생성할 때, 필요한 JdbcTemplate 객체를 찾아서 자동으로 주입해줍니다. 이것이 Spring의 IoC (Inversion of Control) 컨셉입니다.따라서 new 키워드를 사용하여 직접 객체를 생성할 필요가 없고, Spring이 해당 객체를 관리하고 주입해줍니다.미션1일차어노테이션을 미리 정의 해두면 @RequestParam 같은 한 단어만 작성해주면 스프링이 알아서 GET 요청의 쿼리를 메서드의 파라미터로 바꿔준다.어노테이션은 이런 마법 같은 일을 해준다.  2일차문제1Controller에서 getter 메서드가 있는 객체를 반환하면 반환 값이 JSON으로 반환 한다! CalculatorResponse.java public class CalculatorResponse { private int add; private int minus; private int multiply; public CalculatorResponse(int num1, int num2) { this.add = num1 + num2; this.minus = num1 - num2; this.multiply = num1 * num2; } public int getAdd() { return add; } public int getMinus() { return minus; } public int getMultiply() { return multiply; } }  ExController.java@RestController public class ExController { @GetMapping("/api/v1/calc") public CalculatorResponse plusMinusMultiplyCalculator(@RequestParam int num1, int num2) { return new CalculatorResponse(num1, num2); } } Response문제2ExController.java@RestController public class ExController { @GetMapping("/api/v1/day-of-the-week") public DateResponse dayOfTheWeek(@RequestParam String date) { return new DateResponse(date); } } DataResponse.java public class DateResponse { private Enum<DayOfWeek> dayOfTheWeek; public DateResponse(String date) { LocalDate parsed = LocalDate.parse(date); this.dayOfTheWeek = parsed.getDayOfWeek(); } public Enum<DayOfWeek> getDayOfTheWeek() { return dayOfTheWeek; } }  GET 메서드에서 파라미터의 value의 타입은 문자열로 들어오므로 DateResponse의 생성자에서 LocalDate.parse()를 통해 문자열을 LocalDate로 바꿔주었다. 문제3 ExController.java@RestController public class ExController { @PostMapping("/api/v1/multi-number-sum") public int multiNumberSum(@RequestBody CalculatorMultiNumber request) { int[] numbers = request.getNumbers(); int sum = Arrays.stream(numbers).sum(); return sum; } } CalculatorMultiNumber.javapublic class CalculatorMultiNumber { int[] numbers = new int[5]; public int[] getNumbers() { return numbers; } } ResponseResponse크기가 5개인 배열을 선언 하고 body에 길이가 6개를 리스트 넣고 요청해도 6개의 값을 반환한 값을 돌려준다.왜 그런지 이유를 찾고 싶었는데 못찾았다 ㅠㅠ 3일차람다식은 익명 클래스를 좀 더 쉽게 쓸 수 있게 자바8부터 도입된 개념이다. OOP인 자바는 람다식의 도입으로 함수형 프로그래밍도 가능해졌다하루에 몰아서 적으려니까 너무 힘들었다 다음주 회고록은 따로 초고를 그날 그날 적어놔야겠다..이 블로그에 올린 코드와 문제의 출처는 https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90입니다  

백엔드java인프런워밍업클럽스프링spring자바

인프런 워밍업 클럽 0기 - 백엔드 코스 (과제 4)

문제 1. 새로운 과일 정보 생성 APImethod: POSTpath: /api/v1/fruitbody{ "name": "사과", "warehousingDate": "2024-02-21", "price": 1000 } 해결 방법Request Body DTO 생성public class FruitCreateRequest { private String name; private LocalDate warehousingDate; private Long price; public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } }Fruit 객체 생성public class Fruit { private Long id; private String name; private LocalDate warehousingDate; private long price; private boolean isSold; private static Long idCount = 1L; public Fruit(FruitCreateRequest request) { this.id = idCount++; this.name = request.getName(); this.warehousingDate = request.getWarehousingDate(); this.price = request.getPrice(); this.isSold = false; } public void sellFruit(Long id){ isSold=true; } public Long getId() { return id; } public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } public boolean isSold() { return isSold; } }request body를 통해 얻어온 정보를 저장할 수 있도록 했습니다.isSold 맴버 변수로 해당 상품이 팔렸는지 알 수 있도록 했습니다. API 구성 @PostMapping("/api/v1/fruit") public void createFruit(@RequestBody FruitCreateRequest request){ fruits.add(new Fruit(request)); }fruitsCreateRequest를 통해 얻어온 과일정보를 통해 새로운 과일을 생성합니다.fruits에 새롭게 생성된 과일을 넣어줍니다.  문제 2. 과일 팔기method: PUTpath: /api/v1/fruitbody{ "id": 1 } 해결 방법API 구성 @PutMapping("/api/v1/fruit") public void sellFruit(@RequestBody Map<String, Long> request){ fruits.stream().forEach(fruit -> { if(request.get("id")==fruit.getId()){ fruit.sellFruit(); } }); }fruits 리스트를 stream().forEach()를 통해 해당 id의 과일에 팔렸다는 표시를 해줍니다.  문제 3. 과일의 팔린 가격과 아직 팔리지 않은 가격 조회method: GETpath: /api/v1/fruit/statparam: ?name={String} 해결 방법response로 보낼 DTO FruitStatResponse 생성public class FruitStatResponse { private long salesAmount; private long notSalesAmount; public FruitStatResponse(List<Fruit> filteredFruits) { this.salesAmount = 0; this.notSalesAmount = 0; filteredFruits.stream().forEach((fruit -> { if(fruit.isSold()){ salesAmount+=fruit.getPrice(); } else { notSalesAmount+=fruit.getPrice(); } })); } public long getSalesAmount() { return salesAmount; } public long getNotSalesAmount() { return notSalesAmount; } }생성자의 매개변수를 해당 과일 명의 과일만 담긴 리스트를 가져옵니다.해당 과일 중 팔린 것과 팔리지 않은 것의 가격의 총합을 구해줍니다.API 구성 @GetMapping("/api/v1/fruit/stat") public FruitStatResponse getFruitStat(@RequestParam String name){ List<Fruit> filteredFruits = fruits.stream().filter(fruit -> fruit.getName().equals(name)).collect(Collectors.toList()); return new FruitStatResponse(filteredFruits); }stream().filter()를 통해 해당 과일 이름의 과일 리스트를 추려냅니다.FruitStatResponse의 생성자에 필터링된 과일 리스트를 넘겨 가격 총합을 반환합니다.

백엔드springdtorequestbodyrequestparam

스프링 핵심원리 기본편(김영한) 1 - 객체지향 DIP와 스프링 DI, IoC

  객체는 객체와 끊임없이 상호작용한다. 그렇기에 유연한 변경이 가능해야한다. 예를 들어, 자동차라는 상위 클래스를 다양한 자동차 브랜드로 구현될 수 있고, 운전자가 변화해도 자동차는 영향을 받지 않는다. 사용자, 주문, 할인 등 여러 독립적인 특징을 가진 기능은 클래스로 분리하여 각 클래스에서만 수정 및 사용한다.   역할과 구현을 분리 - 인터페이스와 콘크리트 클래스 인터페이스는 안정적이게, 확장이 무한대로 가능하게 설계해야한다.   SOLID 객체지향 설계 원칙 1. SRP 단일책임원칙 - 변경이 용이한 단위적 책임인가2. OCP 개방폐쇄원칙 - 코드의 변경 없이 확장이 가능한가(조립만으로 변경)3. LSP 리스코프 치환 원칙 - 하위 클래스는 인터페이스(상위 클래스)를 위반하지 않아야한다4. ISP 인터페이스 분리 원칙 - 여러 개의 인터페이스를 통해 명확한 기능을 갖고 있고, 대체 가능성이 높은 환경을 구현할 것5. DIP 의존관계 역전 원칙 - 추상화에 의존할 것, 인터페이스(역할)가 중심이 되어야한다. 구현체에 의존하면 다형성을 잃는다(재활용성을 잃는다) 스프링 컨테이너에 객체 지향 적용 객체를 생성하는 역할과 객체를 실행하는 역할을 분리.의존은 인터페이스로 하고, 설정 파일을 통해 구체적인 구현체를 의존 주입구현체 변경 시 설정 파일만 변경하면 된다.(조립)=> 제어의 역전; 어떤 구현체를 사용할 것인지 AppConfig(Spring)가 결정한다. 동적인 인스턴스 의존관계    

객체지향javaSOLIDspringDIIoCDIP강의김영한

대근

반려동물 시장 진출 웹&앱 스프링 개발자 모집

반려동물 시장 진출 웹&앱 스프링 개발자 모집   안녕하세요 현재 서로 믿을 수 있는 스타트업 멤버 구축 되었고 추가로 능력있고 참하신 스프링 개발자 팀원 찾습니다. 현재 모두 메인 잡은 있는 상황이고 사이드 프로젝트로 운영중이라 부담없이 참여 가능합니다. 관심있으신분 연락 부탁드립니다   1. 주제소개: 반려동물 서비스 중 현재 시장에 없는 서비스 2. 현재 진행 단계: 시장분석 및 명확한 주제와 컨텐츠 방향성까지 잡은 상태 3. 모집분야 및 주요업무: 앱 개발자-하이브리드 앱 (웹앱을 네이티브 형태로 감싼 형태) 유사사이트: https://www.pet-friends.co.kr/main/tab/2 4. 모집경력 및 필요스킬: 앱개발 관련하여 경력이 있거나, 최소 스프링을 자유롭게 다룰 수 있는 스킬 필요 vue.js 옵션,java 등 5. 참고기타사항: 기획/디자인/개발/마케팅 포지션 다 구성되어 있으나, 추가 개발자 필요한 상황 6. 문의/연락: 010-4590-4917 카카오: antoniobae1 참고url:IT 웹 앱 마케팅 개발 창업모임 파랑새(Since 2021.10.02 : 네이버 카페 https://cafe.naver.com/lastpick1004 https://lastpick.modoo.at/ https://www.youtube.com/channel/UCpLCToWUvdjsPqkLRZ-wpZA   이 외 어떤 직군이라도 관심있으신 분은 연락 바랍니다.

모바일 앱 개발springjavavuejs

채널톡 아이콘