이야기를 나눠요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
지금 당장 NestJS 백엔드 개발 [ToDo 리스트]
소스코드 링크가 바뀐거 같습니다.
https://github.com/vipick/todo-api-nestjs 이 링크로 이름이 바뀐게 맞을까요?
-
자바 기초부터 마스터하기 with 은종쌤 (Do it 자바 프로그래밍 입문) - Part 1(기초편)
강의 동영상 관련
객체 자신을 가리키는 this 강의에서 9분부터 12분30초 경까지 아무 영상이 나오지 않는 것 같은데 맞나요?확인 부탁드립니다.
-
[신규 개정판] 이것이 진짜 크롤링이다 - 실전편 (인공지능 수익화)
time.sleep(1) 이것 업청 중요 하군요.
selenium 실력 업그레이드 ② - 네이버 지도 크롤링/검색어 입력구현이곳 수업은 아마 혼자했다면 포기했었을 것 같습니다.아무리해도 search = browser.find_element(By.CSS_SELECTOR, 'input.input_search')이것부터 인식을 안해서 왜 안되나 찾고 찾아도 알 수가 없었습니다.time어쩌고 언급이 된 것 같았지만 그것을 읽을 때는 심각하게 생각하지 않고 지나쳐 버려서...포기하고 수업을 들으니 특별한 것이 있을 줄 알았는데...# 검색창 찾아서 입력 search = browser.find_element(By.CSS_SELECTOR, 'input.input_search') search.click() time.sleep(1) # 이것을 안해줘서 인식을 못했음, 사람처럼 인식하도록 함 search.send_keys("강남역 맛집") time.sleep(1) search.send_keys(Keys.ENTER) time.sleep(2) # 3개의 명령어가 순식간에 일어나면 네이버지도는 사람이 아니다라고 인식별것 아닌 것 같은 time.sleep(1) 이것이 해결책이었네요.이 수업하나로 수업료낸 값어치가 있는 것 같습니다..
-
Vue3 완벽 마스터: 기초부터 실전까지 - "실전편"
@popperjs/core 관련 오류와 해결방법
문제 : 프로젝트 실행시 다음과 같은 오류$ yarn dev yarn run v1.22.19 warning package.json: No license field $ vite vite v2.9.15 dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose 7 │ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) : You can mark the path "@popperjs/core" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time. 오후 3:49:06 [vite] error while updating dependencies: Error: Build failed with 1 error: node_modules/bootstrap/dist/js/bootstrap.js:7:98: ERROR: Could not resolve "@popperjs/core"참고로 1_project_settings브랜치의 코드를 checkout한 상태에서 소스코드는 한 줄도 수정하지 않았습니다. 해결방법popperjs/core를 패키지에 추가한다.$npm install @popperjs/core@latest일단 위와 같은 방법으로 해결은 되지만, 문제는 다른 브렌치로 이동(switch/checkout)할 때 마다 똑같은 행위를 해 주어야 한다는 점.git 사용에 익숙치 않는 사람은 로컬 체인지 후 다른 브렌치로 이동하는 것 조차 힘들 수 있다는 점.그래서 프로젝트 실행은 main브랜치에서만 하고, 다른 브랜치의 코드 확인은 체크아웃 받지 않고 하는것을 추천. 원인모르겠습니다.node_modules내부의 bootstrap -> package.json 을 열어보면 devDependencies에 popperjs/core@latest 가 이미 있는데. 해당 모듈을 찾을 수 없다는 에러가 왜 나는걸까요?아무튼 위에 기술한 방법으로 해결하면 됩니다.개발환경은 windows + node v18.x.x 입니다. nvm으로 node v16.0.0 으로 낮춰서 해봐도 같은 증상인 걸 보면 node 버전 문제는 아마도 아닌 것 같습니다.같은 문제로 고민하는 분들께 도움이 되길 바랍니다.
-
현업 실무자에게 배우는 Kaggle 머신러닝 입문 - ML 엔지니어 실무 꿀팁
보스턴 부동산 가격_ax list 문법 문의
figure, ax_list = plt.subplots(nrows=3, ncols=5)figure.set_size_inches(20,20) for i in range(len(full_column_list)): sns.regplot(data=boston_house_df, x=full_column_list[i], y='PRICE', ax=ax_list[int(i/5)][int(i%5)]) ax_list[int(i/5)][int(i%5)].set_title("regplot " + full_column_list[i]) 안녕하세요 수업 즐겁게 듣고 있습니다 : ) 여기서 ax = ax_list 부분 설명 좀 부탁 드리겠습니다. 만약 i를 안쓰고 개별 1개 칼럼에 대해 그래프를 그린다면 ax = 뒤가 뭔지 궁금합니다. 이것만 알아도 위 코드가 이해될 것 같습니다. 감사합니다.
-
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
오타입니다.
3. 메시지, 국제화.pdf 파일 8쪽 상단> "ms.getMessage("hello", null, Locale.KOREA) : locale 정보가 있지만, message_ko 가 없으므로 messages 를 사용"부분에서 "message_ko" 에 s 가 누락된 듯 합니다. messages_ko 아닌지요.
-
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
cs0122, cs0120 계속 두개가 괴롭힙니다..
player.csusing System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour{ // Start is called before the first frame update void Start() { Managers mg = Managers.instance; } // Update is called once per frame void Update() { }}'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' managers.cs using System.Collections;using System.Collections.Generic;using UnityEngine;public class Managers : MonoBehaviour{ static Managers s_Instance; //유일성이 보장된다 public Managers instance { get { Init(); return s_Instance; } } // 유일한 매니저를 갖고온다 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } static void Init() { if (s_Instance == null) { GameObject go = GameObject.Find("@Managers"); if (go == null) { go = new GameObject { name = "@Managers" }; go.AddComponent<Managers>(); } DontDestroyOnLoad(go); s_Instance = go.GetComponent<Managers>(); } }} 도대체 어디가 문제인지 모르겠습니다 보호 수준가지고 왜 자꾸 오류가나는지
-
스프링 핵심 원리 - 기본편
findAllBean과 findApplicationBean의 test를 다르게 적용했는데도 불구하고 값이 계속 같게 나옵니다. 애플리케이션 빈만 출력되야 하는데 전체가 출력되고 있는데 해결방안은 따로 없을까요?
김영한님 강의대로 라이브 코딩했는데 런 했을 시 findAllBean이랑 findApplicationBean의 값이 동일하게 나와서 당황스럽네요... "C:\Program Files\Java\jdk-11.0.14\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar=61288:C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Users\samsung\.m2\repository\org\junit\platform\junit-platform-launcher\1.8.2\junit-platform-launcher-1.8.2.jar;C:\Users\samsung\.m2\repository\org\junit\platform\junit-platform-engine\1.8.2\junit-platform-engine-1.8.2.jar;C:\Users\samsung\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;C:\Users\samsung\.m2\repository\org\junit\platform\junit-platform-commons\1.8.2\junit-platform-commons-1.8.2.jar;C:\Users\samsung\.m2\repository\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\plugins\junit\lib\junit-rt.jar;C:\hello\core\out\test\classes;C:\hello\core\out\production\classes;C:\hello\core\out\production\resources;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-starter-test\2.7.7\204419b38438a398fe43bed95fb835d4ddeb2d2e\spring-boot-starter-test-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-starter\2.7.7\dd06582c2b6b911bdf1be4f3a40e7b63a5ae75d7\spring-boot-starter-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-test-autoconfigure\2.7.7\ae1417f8f60f4ca378568d965e69a7f043e65397\spring-boot-test-autoconfigure-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-test\2.7.7\aff19a025de03eaa2f465dd5c9bc5fe128da69c9\spring-boot-test-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-test\5.3.24\dcad3d7fa11fe832005667ab074a54a9546a59ac\spring-test-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-core\5.3.24\d095c329f30baf2b6d44eccbd2352d7a2f840c72\spring-core-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\com.jayway.jsonpath\json-path\2.7.0\f9d7d9659f2694e61142046ff8a216c047f263e8\json-path-2.7.0.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\jakarta.xml.bind\jakarta.xml.bind-api\2.3.3\48e3b9cfc10752fba3521d6511f4165bea951801\jakarta.xml.bind-api-2.3.3.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.assertj\assertj-core\3.22.0\c300c0c6a24559f35fa0bd3a5472dc1edcd0111e\assertj-core-3.22.0.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.hamcrest\hamcrest\2.2\1820c0968dba3a11a1b30669bb1f01978a91dedc\hamcrest-2.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.jupiter\junit-jupiter\5.8.2\5a817b1e63f1217e5c586090c45e681281f097ad\junit-jupiter-5.8.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.mockito\mockito-junit-jupiter\4.5.1\f81fb60bd69b3a6e5537ae23b883326f01632a61\mockito-junit-jupiter-4.5.1.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.mockito\mockito-core\4.5.1\ed456e623e5afc6f4cee3ae58144e5c45f3b3bf\mockito-core-4.5.1.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.skyscreamer\jsonassert\1.5.1\6d842d0faf4cf6725c509a5e5347d319ee0431c3\jsonassert-1.5.1.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.xmlunit\xmlunit-core\2.9.0\8959725d90eecfee28acd7110e2bb8460285d876\xmlunit-core-2.9.0.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-autoconfigure\2.7.7\8da88afca89ce4b1ab5762e6ca35e1bad196ad47\spring-boot-autoconfigure-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot\2.7.7\1fa59eb2fce0363bdf152d7660b784257bfac99b\spring-boot-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-starter-logging\2.7.7\c71bdb4e93d75b535fef277606868d1d6934c35\spring-boot-starter-logging-2.7.7.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\jakarta.annotation\jakarta.annotation-api\1.3.5\59eb84ee0d616332ff44aba065f3888cf002cd2d\jakarta.annotation-api-1.3.5.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.yaml\snakeyaml\1.30\8fde7fe2586328ac3c68db92045e1c8759125000\snakeyaml-1.30.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-jcl\5.3.24\2b30878663ceed2af07238dc54e92e5bf001438d\spring-jcl-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\net.minidev\json-smart\2.4.8\7c62f5f72ab05eb54d40e2abf0360a2fe9ea477f\json-smart-2.4.8.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.slf4j\slf4j-api\1.7.36\6c62681a2f655b49963a5983b8b0950a6120ae14\slf4j-api-1.7.36.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\jakarta.activation\jakarta.activation-api\1.2.2\99f53adba383cb1bf7c3862844488574b559621f\jakarta.activation-api-1.2.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.jupiter\junit-jupiter-params\5.8.2\ddeafe92fc263f895bfb73ffeca7fd56e23c2cce\junit-jupiter-params-5.8.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.jupiter\junit-jupiter-api\5.8.2\4c21029217adf07e4c0d0c5e192b6bf610c94bdc\junit-jupiter-api-5.8.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy\1.12.20\6ec3b8bccc4c988790d8cde5baad3b95609ef136\byte-buddy-1.12.20.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.12.20\c37341e04a529c7263ff3093ecdceaa4ab39b489\byte-buddy-agent-1.12.20.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\com.vaadin.external.google\android-json\0.0.20131108.vaadin1\fa26d351fe62a6a17f5cda1287c1c6110dec413f\android-json-0.0.20131108.vaadin1.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-context\5.3.24\e48634d7b8f40d4d0fe978830be0247bfc2ff2cd\spring-context-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\ch.qos.logback\logback-classic\1.2.11\4741689214e9d1e8408b206506cbe76d1c6a7d60\logback-classic-1.2.11.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.apache.logging.log4j\log4j-to-slf4j\2.17.2\17dd0fae2747d9a28c67bc9534108823d2376b46\log4j-to-slf4j-2.17.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.slf4j\jul-to-slf4j\1.7.36\ed46d81cef9c412a88caef405b58f93a678ff2ca\jul-to-slf4j-1.7.36.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\net.minidev\accessors-smart\2.4.8\6e1bee5a530caba91893604d6ab41d0edcecca9a\accessors-smart-2.4.8.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.apiguardian\apiguardian-api\1.1.2\a231e0d844d2721b0fa1b238006d15c6ded6842a\apiguardian-api-1.1.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.platform\junit-platform-commons\1.8.2\32c8b8617c1342376fd5af2053da6410d8866861\junit-platform-commons-1.8.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.opentest4j\opentest4j\1.2.0\28c11eb91f9b6d8e200631d46e20a7f407f2a046\opentest4j-1.2.0.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-aop\5.3.24\efd01bc1048a2e1b6a7442fbd78170bc02c342b7\spring-aop-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-beans\5.3.24\e487ea6de09b9a7c36548028feeafa511a593532\spring-beans-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.springframework\spring-expression\5.3.24\ae7410418e7b4bd27a01e3fb1c2fed35b2bc1e84\spring-expression-5.3.24.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\ch.qos.logback\logback-core\1.2.11\a01230df5ca5c34540cdaa3ad5efb012f1f1f792\logback-core-1.2.11.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.apache.logging.log4j\log4j-api\2.17.2\f42d6afa111b4dec5d2aea0fe2197240749a4ea6\log4j-api-2.17.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.ow2.asm\asm\9.1\a99500cf6eea30535eeac6be73899d048f8d12a8\asm-9.1.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.jupiter\junit-jupiter-engine\5.8.2\c598b4328d2f397194d11df3b1648d68d7d990e3\junit-jupiter-engine-5.8.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.objenesis\objenesis\3.2\7fadf57620c8b8abdf7519533e5527367cb51f09\objenesis-3.2.jar;C:\Users\samsung\.gradle\caches\modules-2\files-2.1\org.junit.platform\junit-platform-engine\1.8.2\b737de09f19864bd136805c84df7999a142fec29\junit-platform-engine-1.8.2.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 hello.core.beanfind.ApplicataionContextInfoTest10:18:45.983 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@32ee6fee10:18:46.013 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'10:18:46.237 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'10:18:46.241 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'10:18:46.243 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'10:18:46.246 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'10:18:46.263 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'10:18:46.274 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'10:18:46.295 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'10:18:46.298 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'10:18:46.301 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'name = appConfig object = hello.core.order.AppConfig$$EnhancerBySpringCGLIB$$b9000c86@7966baa7name = memberService object = hello.core.member.MemberServiceImpl@60975100name = memberRepository object = hello.core.member.MemoryMemberRepository@1253e7cbname = orderService object = hello.core.order.OrderServiceImpl@2d36e77ename = discountPolicy object = hello.core.discount.RateDiscountPolicy@61c9c3fd10:18:46.385 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@655ef32210:18:46.386 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'10:18:46.390 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'10:18:46.390 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'10:18:46.391 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'10:18:46.392 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'10:18:46.393 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'10:18:46.394 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'10:18:46.394 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'10:18:46.395 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'10:18:46.395 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor object = org.springframework.context.annotation.ConfigurationClassPostProcessor@58ffcbd7name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor object = org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@555cf22name = org.springframework.context.annotation.internalCommonAnnotationProcessor object = org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@6bb2d00bname = org.springframework.context.event.internalEventListenerProcessor object = org.springframework.context.event.EventListenerMethodProcessor@3c9bfddcname = org.springframework.context.event.internalEventListenerFactory object = org.springframework.context.event.DefaultEventListenerFactory@1a9c38ebname = appConfig object = hello.core.order.AppConfig$$EnhancerBySpringCGLIB$$b9000c86@319bc845name = memberService object = hello.core.member.MemberServiceImpl@4c5474f5name = memberRepository object = hello.core.member.MemoryMemberRepository@2f4205bename = orderService object = hello.core.order.OrderServiceImpl@54e22bddname = discountPolicy object = hello.core.discount.RateDiscountPolicy@3bd418e4종료 코드 0(으)로 완료된 프로세스
-
자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
왜 강의가 벌써 끝나있죠..??
살면서 이렇게 즐겁게 강의 들어본 적이 없는 것 같네요...들으면서 남겼던 질문 3개도 너무 감동적으로 답변해주시고,,62 강의 12시간 1분 동안의 시간동안 태현님과 진행한 공부가 정말 행복했습니다이렇게나 훌륭한 강의 만들어주셔서 감사드리고 항상 행복만 가득하시길 바라겠습니다.공부하는 개발자 최태현님감사합니다!
-
스프링 DB 2편 - 데이터 접근 활용 기술
@Transactional 적용 범위 관련 말씀드립니다!
삭제된 글입니다
-
스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
강의 동영상 앞으로가기
동영상 10 초 앞으로 가기 나 뒤로 가기 안되나요? 강의 중간에 놓치면 처음부터 다시 듣고 있어서요
-
[서울시 주최 무료교육] 뉴딜_콘텐츠 퍼포먼스 마케팅 전문가 과정 모집중(~04/11)
[신청방법][구글폼 신청]링크를 통해 신청해주시면 정상접수 처리 됩니다.
-
[서울시주최 무료]뉴딜_빅데이터 분석 시각화 전문가 과정 모집중(~04/13)
[신청방법](구글폼 링크)를 통하여 신청해주시면 정상 신청처리됩니다.
-
AI 포트폴리오 만들기 - Airbnb 클론 프로젝트
ai 포트폴리오 스터디
안녕하세요! 강사님 이전 게시글에 ai 포트폴리오 스터디를 진행하셨던데, 2기 모집하실 예정이 있는지 궁금합니다!!
-
스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
수업자료
선생님께서 수업에 사용하시는 pdf 파일은 확인할 수 있는 곳이 있나요 ?
-
실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD
실제 실무에서도 새 피쳐를 개발할 때 이런 순서로 개발하시나요?
안녕하세요!아직 열심히 강의를 수강하고있는 주니어 개발자입니다. 😊보다보니 궁금증이 생기는데 실제 실무에서도 이런식으로 새로운 피쳐를 개발하시나요? 사실 TDD 가 실무에서 정석으로 적용하기에는 조금 어렵다는 인식을 가지고 있었어서요. 뚝딱뚝딱 바로 만드시는 걸 보니 오랜 내공이 있어보이시는데 궁금합니다..!
-
스프링 핵심 원리 - 고급편
JDK 동적 프록시 예제를 프록시 체이닝으로 구현...?
들어가기 전에김영한님의 스프링 핵심 원리 - 고급편 수업을 듣고 있던 중에 LogTraceBasicHandler에 필터링을 추가한다는 말을 듣고 이전 강의에서 말씀해주셨던 프록시 체이닝이 생각이 나서 "FilterHandler 이후에 LogTraceHandler 로 이어지는 프록시 체이닝을 보여주시려나" 보다 하고 있는데 그 둘을 합친 LogTraceFilterHandler을 생성하셔서 구현하시길래 "어라, JDK 동적 프록시는 프록시 체이닝으로 하기 까다로운가?" 라는 생각이 들었습니다.그렇다면 구현해보면 알 것 같았기 때문에, 한 번 저의 식으로 구현을 해보았습니다. 그리고 아래의 내용은 스프링 AOP와 CGLIB의 진도를 나가기 전에 작성되었습니다.목표강의 예제의 구조는 다음과 같습니다./** * JDK 동적 프록시 사용<br> * - {@link InvocationHandler} JDK 동적 프록시에 로직을 적용하기 위한 Handler<br> * - {@link PatternMatchUtils#simpleMatch}로 WhiteList 기반 URL 패턴 필터링 */ @Slf4j @RequiredArgsConstructor public class LogTraceFilterHandler implements InvocationHandler { private final Object target; private final LogTrace logTrace; private final String[] patterns; @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { // patterns 에 해당 메서드 이름이 없다면, 바로 목표로 이동 if (!PatternMatchUtils.simpleMatch(patterns, method.getName())) { return method.invoke(target, args); } // LogTrace 로직 실행 TraceStatus status = null; try { String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()"; status = logTrace.begin(message); Object result = method.invoke(target, args); logTrace.end(status); return result; } catch (Exception e) { logTrace.exception(status, e); throw e; } } } 보면, 필터를 수행하는 로직과 로깅을 수행하는 로직이 하나의 메서드로 합쳐져 있습니다. 물론, 저 두 개의 로직을 메서드로 따로 빼면 될 일이긴 합니다만… 저의 SRP 영혼이 슬프게 울고 있더군요.그리고 이전 수업 내용에 프록시의 장점 중 프록시 체이닝이란 것도 있기도 했고, Spring의 Filter도 이런식으로 구현되어 있을거니 나눠봤습니다.제가 구현하고자하는 내용은 아래와 같습니다.흐름만 봐서는 그냥 위의 코드와 동일하지만, 중요한 점은 필터를 담당하는 객체와 로깅을 담당하는 객체가 분리되었다는 것입니다. 그럼 구현을 한 번 해보죠.구현FilterHandler/** * 타겟의 메서드 이름을 필터링하는 Handler<br> * {@link PatternMatchUtils#simpleMatch}를 이용하여 패턴 검증<br> * - 해당 메서드의 이름이 패턴과 일치한다면: {@link #nextHandler}<br> * - 해당 메서드의 이름이 패턴과 일치하지 않는다면: {@link #target} * * @author MinyShrimp * @see Proxy * @see InvocationHandler * @see PatternMatchUtils#simpleMatch(String[], String) * @since 2023-03-02 */ public class FilterHandler implements InvocationHandler { private final Object target; private final Object nextHandler; private final String[] methodPatterns; /** * @param target 최종 목표 구현체 * @param nextHandler 다음 ProxyHandler * @param methodPatterns 필터링을 원하는 패턴 목록 - {@link PatternMatchUtils#simpleMatch} */ public FilterHandler( Object target, Object nextHandler, String[] methodPatterns ) { this.target = target; this.nextHandler = nextHandler; this.methodPatterns = methodPatterns; } @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { Object next = PatternMatchUtils.simpleMatch(methodPatterns, method.getName()) ? nextHandler : target; return method.invoke(next, args); } } 필터를 담당하는 프록시 핸들러입니다. 이 핸들러는 단순히 입력받은 패턴을 조사해 맞으면 다음 핸들러로, 맞지 않으면 바로 목표 구현체로 이동되도록 구현되었습니다.생성자를 보시면 알 수 있겠지만, 최종 목표 구현체(여기서는 OrderControllerV1Impl), 다음 프록시 핸들러(여기서는 LogTraceHandler), 그리고 패턴 패칭을 원하는 문자열 배열을 받습니다.이 예제와는 상관없지만, 개인적으로는 편의를 위해 생성자를 하나 더 만들어서 methodPattern이 배열이 아닌 하나의 문자열만 받을 수 있도록 구현해도 괜찮다고 생각이 듭니다. 하지만, 당장은 사용하지 않기 때문에 제거를 했습니다.그리고 final 맴버변수를 받기 위해 @RequiredArgsConstructor를 사용해도 괜찮지만, 그렇게 하게되면 위와 같이 주석을 남길 수 없기 때문에 사용하지 않았습니다.LogTraceHandler/** * Logging Handler<br> * {@link LogTrace}를 이용하여 로그 출력 * * @author MinyShrimp * @see Proxy * @see InvocationHandler * @see LogTrace * @see ThreadLocalLogTrace * @since 2023-03-02 */ public class LogTraceHandler implements InvocationHandler { private final Object target; private final LogTrace logTrace; /** * @param target 목표 구현체, 다음 ProxyHandler * @param logTrace {@link LogTrace} 구현체 */ public LogTraceHandler( Object target, LogTrace logTrace ) { this.target = target; this.logTrace = logTrace; } @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { TraceStatus status = null; try { String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()"; status = logTrace.begin(message); Object result = method.invoke(target, args); logTrace.end(status); return result; } catch (Exception e) { logTrace.exception(status, e); throw e; } } } 로깅을 담당하는 프록시 핸들러입니다. 이 핸들러는 기존 강의에서 사용된 LogTrace를 받아 로그 메시지를 출력하는 역할을 수행합니다. 강의에서 제작한 LogTraceBasicHandler와 동일하기 때문에 설명을 생략합니다.다만, 정말 개인적인 아쉬움이긴 합니다만, 위의 TraceStatus를 받아옴에 있어서 단순히 저 Exception 하나 때문에 status 변수를 try 외부에 null로 선언하고 재할당 해주는 부분이 너무 아쉬웠습니다. 이를 해결하기 위해선 몇가지 방법이 있긴 합니다만, 이전 시간에 배운 ThreadLocal로 한 번 바꿔보겠습니다. ( 단순히 멤버 변수로 할당하면 동시성 문제가 발생합니다. )public class LogTraceHandler implements InvocationHandler { // 동시성 문제를 해결하기 위해 ThreadLocal 사용 private final ThreadLocal<TraceStatus> thStatus = new ThreadLocal<>(); // 중간 부분 생략 @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { try { String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()"; TraceStatus status = logTrace.begin(message); // 여기서 생성 thStatus.set(status); // ThreadLocal에 저장 Object result = method.invoke(target, args); logTrace.end(status); return result; } catch (Exception e) { TraceStatus status = thStatus.get(); // ThreadLocal에서 값을 가져옴 logTrace.exception(status, e); throw e; } finally { if (logTrace.isFirstLevel()) { thStatus.remove(); // 사용을 마치면 제거하자. } } } } 재할당하는 부분이 사라졌습니다! 이제 status를 받아오기 위해 ThreadLocal.get()을 사용하면 됩니다.그런데 여기서 주의사항이 있습니다. ThreadLocal의 사용이 끝나면 반드시 remove를 통해 지워줘야 합니다. 그래서 위와 같이 finally를 이용해 TraceId의 Level이 0인지 확인하고 0이면 remove를 하도록 작성해보았습니다. TraceId는 LogTrace가 가지고 있으니 넘겨주면 되겠군요.public class ThreadLocalLogTrace implements LogTrace { // 중간 생략 // LogTrace 인터페이스에도 추가해줍니다. @Override public boolean isFirstLevel() { return traceIdHolder.get().isFirstLevel(); // 그대로 넘겨줍니다. } } 좋습니다. 해결이 된 것 같군요. …과연 그럴까요? 이것 또한 버그가 있습니다.TraceId 는 ThreadLocalLogTrace에서 ThreadLocal로 잡고 있는 값입니다. 이것 또한 우리는 이전 시간에서 remove를 해주었습니다.public class ThreadLocalLogTrace implements LogTrace { // 중간 생략 /** * 이전 TraceID 로 전환<br> * - {@link #complete}에서 호출 */ private void releaseTraceId() { TraceId traceId = traceIdHolder.get(); if (traceId.isFirstLevel()) { traceIdHolder.remove(); // TraceId의 ThreadLocal을 제거한다. } else { traceIdHolder.set(traceId.createPreviousId()); } } } 겉보기에는 문제가 없어보입니다. 맞습니다. 평소에는 문제가 없습니다. 그런데 FirstLevel이 0이 되었을 때 문제가 발생합니다. 찬찬히 살펴보죠.위의 코드에서는 TraceStatus를 제거할 때는 finally에서 진행되고, TraceId를 제거할 때는 releaseTraceId에서 제거됩니다. 그리고 이 releaseTraceId는 end()와 exception()메서드에서 실행됩니다!즉, TraceStatus를 제거하기 위해 isFirstLevel() 메서드에서 traceIdHolder.get()을 사용하면, null을 리턴합니다. 그리고, null.isFirstLevel()은 NPE를 발생시킵니다. 그래서 아래와 같이 수정되어야 합니다.@Override public boolean isFirstLevel() { // releaseTraceId에서 제거되었다면 TraceId의 Level도 0이라는 소리. return traceIdHolder.get() == null; } DynamicProxyConfig/** * JDK 동적 {@link Proxy}를 스프링 빈으로 등록하기 위한 설정 파일 * * @author MinyShrimp * @see Proxy * @see FilterHandler * @see LogTraceHandler * @since 2023-03-02 */ @Configuration public class DynamicProxyConfig { /** * {@link FilterHandler}에서 사용하는 필터링 조건들, Whitelist 방식. */ private static final String[] METHOD_PATTERNS = { "request*", "order*", "save*" }; /** * @param target 최종 목표 구현체, 예) {@link OrderControllerV1Impl} * @param logTrace {@link LogTrace} * @return {@link FilterHandler} -> {@link LogTraceHandler} -> {@link OrderControllerV1Impl} */ private static Object filterLogProxyFactory( Object target, LogTrace logTrace ) { // 타겟이 상속받은 인터페이스들 중 첫 번째를 가져온다. // 해당 예제의 목표 타겟인 app.v1 들의 구현체들은 모두 인터페이스를 하나만 가지고 있기 때문에 가능하다. Class<?> superIntf = target.getClass().getInterfaces()[0]; // LogTraceProxy 생성 Object logTraceProxy = Proxy.newProxyInstance( superIntf.getClassLoader(), new Class[]{superIntf}, new LogTraceHandler(target, logTrace) ); // LogTraceProxy, 목표 타겟을 담은 FilterProxy 생성 return Proxy.newProxyInstance( superIntf.getClassLoader(), new Class[]{superIntf}, new FilterHandler(target, logTraceProxy, METHOD_PATTERNS) ); } /** * @return {@link OrderControllerV1Impl}의 Proxy */ @Bean OrderControllerV1 orderControllerV1(LogTrace logTrace) { OrderControllerV1Impl target = new OrderControllerV1Impl(orderServiceV1(logTrace)); return (OrderControllerV1) filterLogProxyFactory(target, logTrace); } /** * @return {@link OrderServiceV1Impl}의 Proxy */ @Bean OrderServiceV1 orderServiceV1(LogTrace logTrace) { OrderServiceV1 target = new OrderServiceV1Impl(orderRepositoryV1(logTrace)); return (OrderServiceV1) filterLogProxyFactory(target, logTrace); } /** * @return {@link OrderRepositoryV1Impl}의 Proxy */ @Bean OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) { OrderRepositoryV1Impl target = new OrderRepositoryV1Impl(); return (OrderRepositoryV1) filterLogProxyFactory(target, logTrace); } } 위에서 제작한 FilterHandler와 LogTraceHandler를 스프링 빈으로 등록하기 위한 설정 클래스입니다. 기본 베이스는 기존 강의에서 제작한 DynamicProxyFilterConfig와 동일합니다. 차이점이 있다면 각 빈에서 프록시 핸들러를 반환할때 발생하던 중복 코드를 filterLogProxyFactory() 메서드로 합쳐준 것 뿐입니다.그렇기 때문에 가장 중요한 filterLogProxyFactory()에 대해 설명하겠습니다./** * 이 메서드를 사용하기 위해선 FilterHandler와 LogTraceHandler는 * target의 첫 번째 인터페이스를 기반으로 프록시를 생성할 수 있어야 한다. * * @param target 1. 최종 목표 구현체, 예) {@link OrderControllerV1Impl} * @param logTrace {@link LogTrace} * @return {@link FilterHandler} -> {@link LogTraceHandler} -> {@link OrderControllerV1Impl} */ private static Object filterLogProxyFactory( Object target, LogTrace logTrace ) { // 2. 타겟이 상속받은 인터페이스들을 가져온다. Class<?>[] supIntfs = target.getClass().getInterfaces(); // 3. LogTraceProxy 생성 Object logTraceProxy = Proxy.newProxyInstance( supIntfs[0].getClassLoader(), supIntfs, new LogTraceHandler(target, logTrace) ); // 4. LogTraceProxy, 목표 타겟을 담은 FilterProxy 생성 return Proxy.newProxyInstance( supIntfs[0].getClassLoader(), supIntfs, new FilterHandler(target, logTraceProxy, METHOD_PATTERNS) ); } 이 메서드의 과정은 다음과 같습니다.타겟 객체(OrderControllerV1Impl)를 파라미터로 받아온다.타겟이 상속받은 인터페이스들의 정보(supIntfs)를 가져온다.그 정보를 이용해 LogTraceHandler의 프록시(logTraceProxy)를 생성한다.FilterHandler의 프록시를 생성하고 반환한다.기존 코드와 다른 점은 newProxyInstance()를 사용할 때, ClassLoader를 타겟의 첫 번째 인터페이스로 가져오는 것과, 두 번째 인자에 타겟의 모든 인터페이스들을 넣어 주는 부분입니다.이렇게 한 이유는 다름이 아니라, 이 메서드를 사용하는 모든 타겟 객체가 하나의 인터페이스만 상속받으며, 그 인터페이스를 이용해 핸들러들을 프록시로 만들어도 문제가 없기 때문입니다. 만약, 여러 개의 인터페이스를 상속받으며, 첫 번째 인터페이스를 기반으로 프록시를 생성하면 안되는 경우에는 위의 메서드를 사용할 수 없습니다. 그 때는 파라미터를 하나 추가해서 ClassLoader를 받아오면 됩니다.추가로 주의할 점은 FilterHandler 생성자에 LogTraceHandler를 주입한 것이 아닌, LogTraceProxy를 주입했다는 점입니다. LogTraceHandler도 invoke 함수가 제공되지만, 이는 Method의 invoke 함수와 무관하기 때문에 작동되지 않습니다. (예외가 발생합니다.)참고로, MainApplication에 해당 설정 파일을 등록하는 부분은 생략했습니다.완성…?이 구조를 완성했습니다. 구조 자체는 간단합니다만, 이쁘게 만들려다보니 신경써야할 부분이 좀 많았습니다.물론, 처음에 코드를 작성할 때 TraceStatus를 멤버 변수로 등록했다가 동시성 문제도 터지고, 위에서 설명한 finally에서도 NPE가 발생하기도 하고, FilterHandler에 LogTraceProxy를 넣어야하는데 LogTraceHandler를 넣어서 체이닝이 안되기도 했습니다만 구현해 놓고 보니 뿌듯합니다.하지만 아직 미심쩍은 부분과 아쉬운 부분이 보입니다.“ThreadLocal가 좋은건 알겠는데 이렇게 많이 사용해도 괜찮나…?”ThreadLocal의 가장 큰 주의점은 쓰레드 풀 환경에서 remove를 해주지 않는다면 다른 유저가 그 정보를 볼 수 있다는 점입니다. 또한, ThreadLocalMap은 크기가 커지면 커질 수록 2배의 크기로 할당됩니다. 지금까지는 별 문제가 없어보이지만, 다른 프로그래머가 코드를 수정하여 remove가 실행이 안되게 되거나, 비즈니스 로직 변경으로 인해 급하게 수정하다가 remove를 놓치게 되면 위의 문제는 꽤 심각하게 발생합니다. 이때는 어떻게 대처를 해야하나요? 아니면 위 문제는 별로 신경쓰지 않아도 되나요?“지금처럼 핏한 상황은 잘 작동하지만, 이 코드를 확장하려면 고칠 부분이 많네…”꽤 만족할만한 코드 퀄리티라고 생각은 합니다만, 위의 코드를 재사용하여 확장해야 하는 상황이 온다면 고쳐야할 부분이 눈에 들어옵니다. 일례로, 목표 타겟의 인터페이스 상속이 늘어나고 순서가 바뀌면 위의 코드의 filterLogProxyFactory는 더이상 사용할 수 없습니다.그리고 Handler가 늘어나면 설정 파일에서 그에 맞게 주입을 먼저 해주어야합니다. 이는 전략 패턴의 단점과도 연결됩니다. 또한, FilterHandler 와 같이 분기점에 따른 Proxy 변경도 많아지게 되면 일반화를 진행해야합니다.(BranchHandlerFactory 와 비슷한 이름으로..)정리사실, 위의 로직들은 JDK 동적 프록시가 아닌 다른 방법으로 구현하는게 맞습니다. 스프링 MVC에서 배운 필터와 인터셉터도 있고, (저는 아직 진도를 안 나갔습니다 만은)앞으로 배울 스프링 AOP가 해결 방법이 될 수도 있습니다. 그럼에도 이렇게 시간을 들여 글을 쓰는 이유는 다음과 같습니다.코드를 구현할 때 어떤 방식으로 구현 하는지 생각을 정리하고 기록을 남기기 위함 이었습니다.혼자서 코드를 작성하는 것은 언제나 자신과의 싸움을 하고 있다는 말과 같습니다. 보통 이런 상황에서 누군가에게 피드백을 받기란 요원한게 사실입니다. 그리고 그 기간이 길어지면 길어질수록 현재 자신의 위치가 어느 정도인지 짐작조차 할 수 없게 되고 다른 사람에게 물어볼때 어떤 방법으로 물어봐야하는지 모를 수 밖에 없습니다.“지금 짜고 있는 코드가 좋은 코드인가?” 과연 어떤 프로그래머가 이 생각을 하지 않겠냐 만은, 혼자서 코드를 작성하면 정말 나쁜 코드를 작성 하더라도 위에 대한 판단을 내릴 수가 없습니다. 또한, 나쁜 코드를 벗어나서 좋은 코드로 향하는 방법을 알고 싶어도 키워드를 모르니 방법이 없는 거지요.그래서 저의 코드 구현 방식을 공유하고 다른 분들에게 피드백을 받기 위해 이 글을 작성했습니다.현재 구현한 이 방법보다 더 좋은 방법이 무엇인지, 어떤 사이트 이펙트가 발생하는지, 내가 생각하고 있는 개념이 맞는지, JavaDoc 쓰는 방법은 올바른지, 등등 알고 싶은게 많습니다. (그러니까 비-법 소스 주세요!!!)꼭 긴글이 아니더라도 지나가는 말처럼 짧은 키워드만 툭툭 던져주셔도 저같이 공부하시는 분들에게는 많은 도움이 됩니다. 긴 글 읽어주셔서 감사드리며, 저와 같은 다른 취준생 여러분들도 다 같이 화이팅입니다. ^^7
-
10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
게임회사 코테 준비
안녕하세요.게임회사를 준비중인데 게임회사 코테도 이 강의만으로 커버가 가능할까요?아니면 이 강의에서 다루지 않은게 있다면 무엇이 있을까요?
-
나도코딩의 자바 기본편 - 풀코스 (20시간)
강사님, 혹시 자바 관련해서 다른 내용을 추가로 다루실 예정이 있으실까요?
보통 자바 강의를 들으시는 분들은 대다수 다음 테크로 스프링이나 안드로이드로 넘어가려는 목적을 가지신 분들이 많을 것 같은데요! 보아하니 대체적으로 스프링 강의에서는 Optional 같은 문법들을 많이 사용하는 것 같더라구요. 해서 혹시 다음 기획하시는 강의나 아니면 현재 강의에서 추가적인 내용을 더 다루실 계획이 있으신지 궁금하여 문의드립니다!
-
따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
context-api쪽 auth.tsx를 제 나름대로 리팩토리 해보았습니다.
[auth.tsx]import { User } from '@/shared/interfaces/user.interface'; import { createContext, FC, PropsWithChildren, useContext, useReducer, Dispatch, ReactNode, } from 'react'; interface AuthState { authenticated: boolean; user: User | undefined; loading: boolean; } export type AuthAction = | { type: 'LOGIN'; payload: User } | { type: 'LOGOUT' } | { type: 'STOP_LOADING' } | { type: undefined }; const initialState: AuthState = { authenticated: false, user: undefined, loading: true, }; const AuthContext = createContext<{ state: AuthState; dispatch: Dispatch<AuthAction>; }>({ state: initialState, dispatch: () => null, }); const authReducer = (state: AuthState, action: AuthAction): AuthState => { switch (action.type) { case 'LOGIN': return { ...state, authenticated: true, user: action.payload, }; case 'LOGOUT': return { ...state, authenticated: false, user: undefined, }; case 'STOP_LOADING': return { ...state, loading: false, }; default: throw new Error(`Unknown action type: ${action.type}`); } }; const AuthProvider: FC<PropsWithChildren<{ children?: ReactNode }>> = ({ children, }) => { const [state, dispatch] = useReducer(authReducer, initialState); return ( <AuthContext.Provider value={{ state, dispatch }}> {children} </AuthContext.Provider> ); }; const useAuthStateDispatch = () => useContext(AuthContext); export { AuthProvider, useAuthStateDispatch }; [login.tsx]import InputGroup from '@/components/ui/field/InputGroup'; import axios from 'axios'; import { AuthAction, useAuthStateDispatch } from 'context/auth'; import { NextPage } from 'next'; import Link from 'next/link'; import { useRouter } from 'next/router'; import React, { FormEvent, useState } from 'react'; const LoginPage: NextPage = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [errors, setErros] = useState<any>({}); const router = useRouter(); const { dispatch } = useAuthStateDispatch(); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); try { const res = await axios.post( '/auth/login', { password, username }, { withCredentials: true }, ); dispatch({ type: 'LOGIN', payload: res.data?.user }); router.push('/'); } catch (error: any) { console.log(error); setErros(error.response.data || {}); } }; return ( <div className="bg-white"> <div className="flex flex-col items-center justify-center h-screen p-6"> <div className="w-10/12 mx-auto md:w-96"> <h1 className="mb-2 text-lg font-medium">로그인</h1> <form onSubmit={handleSubmit}> <InputGroup placeholder="Username" value={username} setValue={setUsername} error={errors.username} /> <InputGroup placeholder="Password" value={password} setValue={setPassword} error={errors.password} /> <button className="w-full py-2 mb-1 text-xs font-bold text-white uppercase bg-gray-400 border border-gray-400 rounded"> 로그인 </button> </form> <small> 아직 아이디가 없나요? <Link href="/register" className="ml-1 text-blue-500 uppercase"> 회원가입 </Link> </small> </div> </div> </div> ); }; export default LoginPage; 누군가에겐 도움이 되지 않을까해서 남겨 보아요!