묻고 답해요
131만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
질문이있습니다.
현 시점에서 nest공식문서 가보면어센틱케이션에 passport는 없어지고그냥 @nestjs/jwt만 있는것으로 바뀌어있고passport는recipes카테고리로 이동되어있고install 도 $ npm install --save @nestjs/passport passport passport-local $ npm install --save-dev @types/passport-local이렇게 나와있는것으로 보이는데그냥 위 passport따라가면되나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
비주얼스튜디오코드 폴더 복사 붙이기 문제
폴더 복사 붙여넣기 후 이름 변경시아래와 같은 에러가 뜬다면뭘 점검해 봐야 하나요?복사 붙이기로 새로 생성된 폴더 안의,파일을 열거나 하지 않았는데,복사 완료 후 폴더명 변경시 왜 오류가 나는 걸까요?답변 주시면 감사하겠습니다. Error: '03-04-rest-api-with-express-board-'을 (를) 'section03'(Error: EBUSY: resource busy or locked, rename 'c:\study\node_codecamp\class\section03\03-04-rest-api-with-express-board-' -> 'c:\study\node_codecamp\class\section03\03-04-rest-api-with-express-swagger')(으)로 이동할 수 없습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
섹션22 과제5번 질무이있습니다
아래 코드를 실행할때마다 오류가 발생해서 여쭤봤습니다. 그전에 startStandaloneServer가 없을때에는 오류는 안나왔지만 grapql페이지로 접속하였을때 빨간불이 들어오고 서버가 제대로 접속되지않아서 방법을 바꾸니 typeerror가 발생했는데 어디에서도 server.addPlugin을 작성하지않았고 공식페이지에서의 방법도 해봤는데 왜 오류가 나는지 찾지를 못했습니다import { ApolloServer } from "apollo-server"; import { startStandaloneServer } from "@apollo/server/standalone"; const typeDefs = `#test # Board에 관한 Query는 # 로직 내에 Query가 없는 채로 실행했을 때 나타나는 # "Error: Query root type must be provided." 에러 방지를 위한 것입니다. type BoardReturn { number: Int writer: String title: String contents: String } type Query { fetchBoards: [BoardReturn] } type Mutation { # 1. 아래에 createTokenOfPhone API의 요청 데이터 타입을 지정해 주세요. createTokenOfPhone(phone : Int) : String } `; const resolvers = { Query: { fetchBoards: (_, args) => { return [ { number: 1, writer: "철수", title: "제목입니다", contents: "내용입니다", }, { number: 2, writer: "영희", title: "좋은 날씨입니다", contents: "내용입니다", }, ]; }, }, Mutation: { createTokenOfPhone: (_, args) => { // 2. 아래 로직을 만들어 주세요. // (힌트: phone.js 내에 존재하는 함수들을 사용해서 로직을 완성해 주시면 됩니다. // 로직 구성이 어려우신 분들은 rest_api 폴더 내에 존재하는 index.js 파일을 참고해 주세요.) // 2-1. 휴대폰번호 자릿수 맞는지 확인하기 const phone = args.phone; if (phone.length > 10 || phone.length < 10) { return "핸드폰 번호 재확인 해주세요"; } // 2-2. 휴대폰 번호 자릿수가 맞다면 핸드폰 토큰 4자리 만들기 const result = String(Math.floor(Math.random() * 1000)).padStart(4, "0"); // 2-3. 만든 토큰을 핸드폰번호에 토큰 전송하기 return `token : ${result}`; }, }, }; const server = new ApolloServer({ typeDefs, resolvers, }); await startStandaloneServer(server); // server.listen(3000).then(({ url }) => { // console.log(`🚀 Server ready at ${url}`); // });
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
nodejs import 시 확장자 생략시 오류
//index.js import express from "express" const app = express() import { checkPhone } from "./phone.js" app.get("/", function (req, res) { res.send("Hello World") }) app.get("/phone", (req, res) => { const p = checkPhone() res.send(p) }) app.listen(3000) //phone.js export function checkPhone() { return true } 확장자를 생략해도 되는걸로 알고있었는데,확장자를 생략하면 왜 에러가 날까요?ERR_MODULE_NOT_FOUND에러가 나는 이유가 궁금합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
테스트 동시성 관련 질문드립니다
안녕하세요 강의 다 듣고 기능 추가해 가며 공부를 하고 있습니다. 그러다 막히는 부분이 있어 질문드리는데 강의 내용을 조금 벗어 나는거 같아 질문을 드려도 될지 모르겠는데 괜찮으시다면 답변 주시면 감사하겠습니다. @Transactional public OrderResponse createOrder(OrderCreateServiceRequest request, LocalDateTime registeredDateTime) { List<String> productCodes = request.getProductCodes(); List<Product> products = findProductsBy(productCodes); Member member = memberRepository.findByPhoneNumber(request.getPhoneNumber()).get(); deductStockQuantities(products); Order order = Order.create(products, member, registeredDateTime); return OrderResponse.of(orderRepository.save(order)); }order를 생성하는 부분에서 재고 감소 되는 부분을 동시성 처리를 해보려 하는데 테스트 코드에선 deductStockQuantities로 넘어가서 findAllByProductCodeIn 만 한번 돌고 롤백이 되더라구요. @Test public void create_order_with_concurrent_5_request() throws InterruptedException { //given createProducts(); OrderCreateServiceRequest request1 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); OrderCreateServiceRequest request2 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); OrderCreateServiceRequest request3 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); int numberOfThreads = 3; ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); CountDownLatch latch = new CountDownLatch(numberOfThreads); //when executorService.submit(() -> { try { orderService.createOrder(request1, LocalDateTime.now()); }finally { latch.countDown(); } }); executorService.submit(() -> { try { orderService.createOrder(request2, LocalDateTime.now()); }finally { latch.countDown(); } }); executorService.submit(() -> { try { orderService.createOrder(request3, LocalDateTime.now()); }finally { latch.countDown(); } }); latch.await(); //then List<Stock> stocks = stockRepository.findAllByProductCodeIn(List.of("A002","A003")); assertThat(stocks).hasSize(2) .extracting("productCode", "quantity") .containsExactlyInAnyOrder( tuple("A002", 7), tuple("A003", 7) ); } @Lock(LockModeType.PESSIMISTIC_WRITE) List<Stock> findAllByProductCodeIn(List<String> productCodes);테스트 코드는 구글링해서 넣어보았는데 이런 부분 관련해서 따로 배운게 없어 잘 안되더라구요. 락도 걸어보고 했는데 어디서 안되는지 잘 모르겠어서 질문드립니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
section22 의 CORS 관련 질문
안녕하세요!해당 수업에서 CORS 에러가 발생한 이유를 다른 작성자 분께서 질문해주셨는데, 강사님께서 프론트엔드와 백엔드의 포트가 달라서 그렇다고 대답해주셨습니다. 백엔드에서는 app.listen을 통해 포트를 설정을 해주었지만, 프론트엔드에서는 포트를 설정한 적이 없는데 포트번호를 어떻게 알 수 있나요..?설정하는 방법이 따로 있는건가요?그리고 이전수업에서 한 컴퓨터에서는 프론트엔드와 백엔드의 포트번호가 달라야한다고 말씀하셨는데, 그렇다면 하나의 컴퓨터에서는 same origin이 될 수 없는건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
파이썬이나 자바도 공부해야하나요?
이 강의만 들으면 되나요?아니면 파이썬이나 자바는 제가 따로 공부해야하나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
버전 2으로 듣기
버전1으로 듣다가 버전2가 있다는 걸 알게 되어서 버전 1 말고 버전2으로 들어도 되나요?버전2는 처음부터 들으려고요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
oauth 관련 질문 있어요.
안녕하세요. 강의 잘 듣고 있습니다.user - service - google가 있다고 했을 때,user가 oauth code grant방식으로 login을 하면 service가 callback을 통해 code를 받게되고, 그 정보와 다른 정보를 조합하여 google로 부터 access token과 refresh token을 받아올텐데요.여기서 token을 어떤식으로 처리하는지 궁금합니다.service가 google로 부터 받은 token들을 app으로 전달하는 건가요? 그렇다면 app은 token이 만료되면 google에 직접 토큰을 다시 요청하는걸까요? 아니면 service를 통해 relay를 해서 토큰을 다시 요청할까요? service는 token이 올바른건지는 검증할 수 없으니 db에 저장을 해 두고 나중에 같은 토큰을 user로부터 받았을때 같은지 비교해서 허용할 지 말지 알 수 있을거 같은데 이렇게 하는건가요?아니면 service가 직접 새로운 토큰들을 생성해서 관리하는게 좋을까요?그리고 public app에서 보안을 더 강화하기 위해 pkce방식도 쓰는거 같던데 실제 많이 쓰시는지 궁금하네요.답변 부탁드립니다.감사합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
ESLint 에러가 안나옵니다.
08-ESLint & Prettier 강의를 보는 중인데 5분 27초 경에 강의 에서 나오는 eslint 에러가 저는 안나옵니다. eslint가 적용이 안되고 있는걸까요?강의 화면제 vscode 화면
-
미해결Practical Testing: 실용적인 테스트 가이드
이 경우, @BeforeEach에 fixture를 구성해도 될까요?
지금 OrderServiceTest 클래스의 createOrder(), createOrderWithStock(), ... 메서드들은 주문을 생성하기 위해서 given절에서 모두 동일하게 product1, 2, 3을 먼저 생성해주는 fixture가 있습니다. @Test void createOrder() { // given LocalDateTime registeredDateTime = LocalDateTime.now(); Product product1 = createProduct(HANDMADE, "001", 1000); Product product2 = createProduct(HANDMADE, "002", 3000); Product product3 = createProduct(HANDMADE, "003", 5000); productRepository.saveAll(List.of(product1, product2, product3)); OrderCreateServiceRequest request = OrderCreateServiceRequest.builder() .productNumbers(List.of("001", "002")) .build(); // when OrderResponse orderResponse = orderService.createOrder(request, registeredDateTime); // then assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime", "totalPrice") .contains(registeredDateTime, 4000); assertThat(orderResponse.getProducts()).hasSize(2) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("002", 3000) ); }product를 생성해주는 부분을 @BeforeEach로 빼내려고 하는데, 적절하다고 판단되시는지 궁금합니다. OrderServiceTest의 모든 메서드가 동일한 product를 생성하고는 있지만,각 테스트 메서드에서 "001"이라는 상품이 있는지는 @BeforeEach 메서드까지 스크롤을 왔다갔다하면서 확인해야될거라 생각합니다.(만약 없는 상품을 주문한다면 given절에서 예외가 발생하는 것이기 때문에, 이 부분은 당연히 있는 상품을 주문한다는 것을 보장하고 테스트 코드를 작성해야 할까요?) order에 관한 테스트이기 때문에, product에 대한 부분은 분리해도 될거라 생각하지만, 우빈님의 말씀대로 스크롤을 왔다갔다하면서 @BeforeEach의 메서드를 수시로 보는 것은 안좋다고 느껴져서.. product 생성에 대한 fixture를 분리괜찮다고 생각하시나요?
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
jpa의 더티체킹 사용에 대해서
해당 강의에서 도메인 모델과 영속성 객체를 구분하는 리팩토링을 진행하셨는데요.리팩토링 이후 영속성 컨텍스트를 통한 더티체킹을 활용하지 않고, 대신 save 메서드를 매번 호출해 주시는 방법으로 변경하셨더라구요.도메인 모델과 영속성 객체의 의존성을 제거하기 위해서 더티체킹 기능도 사용하지 않는건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
섹션8 - Spring REST Docs
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요.안녕하세요. 강의를 100% 다 소화하지는 못했지만 일단 거진 한바퀴는 돌리면서 마지막에 문제가 생겨 질문 드립니다.plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id "org.asciidoctor.jvm.convert" version "3.3.2" } group = 'sample' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '11' } configurations { compileOnly { extendsFrom annotationProcessor } asciidoctorExt } repositories { mavenCentral() } dependencies { // Spring boot implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' // test testImplementation 'org.springframework.boot:spring-boot-starter-test' // lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' //테스트에서 lombok 사용 testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' // h2 runtimeOnly 'com.h2database:h2' // Guava implementation("com.google.guava:guava:32.1.1-jre") // RestDocs asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } tasks.named('test') { useJUnitPlatform() } ext { // 전역 변수 snippetsDir = file('build/generated-snippets') } test { outputs.dir snippetsDir } asciidoctor { inputs.dir snippetsDir configurations 'asciidoctorExt' sources { include("**/index.adoc") } baseDirFollowsSourceDir() dependsOn test } bootJar { dependsOn asciidoctor from ("${asciidoctor.outputDir}") { into 'static/docs' } } 일단 build.gradle 내용이구요. 운영체제는 윈도우 입니다.마지막에 build 후 jar파일을 실행하여 localhost:8080/docs/index.html 을 확인해보려고 하니 접속이 되지 않아 확인해보니 resources - static 디렉토리에 index.html이 생성되지 않았음을 확인하였습니다. build 디렉토리에는 생성되어 있었구요.몇번이나 clean 후 build를 통해 build쪽에는 index.html이 생성 되는 것을 확인 하였으나, static 패키지 하위에 docs 패키지 조차 생성되지 않아 제가 뭔가 오타를 낸건지 운영체제 문제인지 질문 드리고자 합니다. 감사합니다.
-
미해결따라하며 배우는 리액트 A-Z
setValue가 함수가 아니라는 오류 발생
TailwindCss 적용하기 실습 중 발생한 문제입니다.setValue is not a function TypeError: setValue is not a function at handleChange (http://localhost:3000/static/js/bundle.js:167:5) at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:11038:18) at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:11082:20) at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:11139:35) at invokeGuardedCallbackAndCatchFirstError (http://localhost:3000/static/js/bundle.js:11153:29) at executeDispatch (http://localhost:3000/static/js/bundle.js:15297:7) at processDispatchQueueItemsInOrder (http://localhost:3000/static/js/bundle.js:15323:11) at processDispatchQueue (http://localhost:3000/static/js/bundle.js:15334:9) at dispatchEventsForPlugins (http://localhost:3000/static/js/bundle.js:15343:7) at http://localhost:3000/static/js/bundle.js:15503:16이러한 오류가 발생하였습니다.아래는 코드입니다.지난 시간에서 딱히 바꾼게 없는데 오류의 원인을 잘 모르겠어서 질문드립니다.App.jsimport React, {useState} from "react"; import "./App.css"; import List from "./components/List"; import Form from "./components/Form" export default function App (){ const [todoData, setTodoData] = useState([]); const [value,setValue] = useState(""); const handleSubmit = (e) =>{ e.preventDefault(); // 전송시 페이지 새로고침 방지 함수 let newTodo = { id : Date.now(), title: value, completed : false }; // 새로운 할 일 데이터의 형성 setTodoData(prev => [...prev,newTodo] ) setValue("") // 원래 할 일에 새로운 할 일 데이터의 update 해주는 setState 함수 } const btnStyle = { color: "#fff", border: "none", padding : "5px 9px", borderRadius: "50%", cursor: "pointer", float:"right" } return( <div className="container"> <div className='todoBlock'> <div className="title"> <h1>To do list</h1> </div> <h1 className='text-3xl font-bold underline'>Hello world!</h1> <List todoData= {todoData} setTodoData = {setTodoData}/> <Form handleSubmit = {handleSubmit} value ={value} setValue = {setValue}/> </div> </div> ); }list.jsimport React from 'react' export default function List({todoData , setTodoData}) { const btnStyle = { color: "#fff", border: "none", padding : "5px 9px", borderRadius: "50%", cursor: "pointer", float:"right" } const handleClick = (id) => { let newTodoData = todoData.filter(data => data.id !== id) console.log('newTodoData',newTodoData); setTodoData(newTodoData); } const getStyle = (completed) => { return { padding : "10px", borderBottom: "1px #ccc solid", textDecoration: completed ? 'line-through' : 'none', } } const handleCompleteChange = (id) => { let newTodoData = todoData.map(data => { if (data.id === id){ data.completed = !data.completed; } return data; }); setTodoData(newTodoData); }; return ( <div> {todoData.map((data)=>( <div style = {getStyle(data.completed)} key = {data.id}> <input type = "checkbox" defaultChecked = {false} onChange = {() => handleCompleteChange(data.id)}/> {data.title} <button style = {btnStyle} onClick = {() => handleClick(data.id)}>X</button> </div> ))} </div> ) }form.jsimport React from 'react' export default function Form(handleSubmit,value,setValue) { const handleChange = (e) =>{ setValue(e.target.value); } return ( <form style = {{display:"flex"}} onSubmit = {handleSubmit}> <input type = "text" name = "value" style = {{flex: '10', padding :'5px'}} placeholder = "해야 할 일을 입력하세요." value ={value} onChange ={handleChange} /> <input type = "submit" value = "입력" className="btn" style ={{flex:'1'}} /> </form> ) }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
QueryFailedError: Table 'user' already exists
안녕하세요 선생님 다름이 아니라 synchronize: true, // 동기화 시켜준다 같게 한다.true을 하게되면 동기화를 시켜주는건데 매번 yarn start:dev을할때마다 QueryFailedError: Table '***' already exists이러한 오류가 나옵니다. 그럼 실행 할때마다 데이터베이스 테이블을 매번 지워야 하는건가요??
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
완강 후 질문이 있습니다~!
안녕하세요 재밌고 알찬 강의 제공해 주셔서 감사합니다.jpa 와 같이 findById 같은 기본적으로 제공해주는 메서드는 따로 테스트를 안해봐도 될거 같은데..queryDsl 식으로 커스텀한 쿼리를 호출할때 테스트 방법은 어떻게 해야할지 궁금합니다.. h2 와 같은 임베디드디비로 쿼리 조건과 데이터가 잘 나오는지 테스트도 짜고, 다른 인터페이스를 상속받은 구현체의 메서드에서 메모리를 사용해 stream 과 filter 식으로 데이터를 반환하는 구현체를 테스트로 다시 넣어야하는지.. h2로 테스트코드를 짜고, 확인 후, 해당 테스트 코드는 지워버려야 하는지.. 강사님은 어떻게 하고 있는지 궁금합니다.. 감사합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
swaggerui에서 execute했는데 console에서는 계속 typeerror로 n.get함수가 없다는 에러가 발생합니다.
javascript문서를 확인했을때는 배열을 객체로쓴다던가 함수를 부를수없는곳에 작성했다는등의 오류라고 나와있는데 아무리 찾아봐도 그오류가 어디에서 나오는지 왜나오는지를 알수없어서 질문올립니다.// index.jsimport express from "express"; import { options, dataCoffee, dataUsers } from "./swagger/config.js"; import cors from "cors"; const app = express(); import swaggerUi from "swagger-ui-express"; import swaggerJSDoc from "swagger-jsdoc"; app.use(express.json()); const swaggerSpec = swaggerJSDoc(options); app.use(cors()); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); app.get("/users", (req, res) => { res.send(dataUsers); }); app.get("/starbucks", function (req, res) { res.send(dataCoffee); }); app.listen(3001); // config.jsexport const options = { definition: { openapi: "3.0.0", info: { title: "swagger-test", version: "1.0.0", }, }, apis: ["./swagger/*-swagger.js"], // files containing annotations as above }; export const dataCoffee = [ { name: "아메리카노", kcal: 5 }, { name: "카푸치노", kcal: 125 }, { name: "헤이즐넛", kcal: 85 }, { name: "카라멜마키아또", kcal: 225 }, { name: "휘핑크림추가", kcal: 115 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, { name: "아메리카노", kcal: 5 }, ]; export const dataUsers = [ { email: "aaa@gmail.com", name: "짱구", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, { email: "aaa@gmail.com", name: "짱구2", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, { email: "aaa@gmail.com", name: "짱구3", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, { email: "aaa@gmail.com", name: "짱구4", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, { email: "aaa@gmail.com", name: "짱구5", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, { email: "aaa@gmail.com", name: "짱구6", phone: "010-2293-3333", personal: "222012-2210392", prefer: "https://google.com", }, ];// all-swagger.js/** * @swagger * /starbucks: * get: * summary: 커피 * tags: [Coffee] * parameters: * name: String * kcal: int * responses: * 200: * description: 성공 * content: * application/json: * schema: * type: array * items: * properties: * name: * type: String * example: 아메리카노 * kcal: * type: int * example: 5 */ /** * @swagger * /users: * get: * summary: 유저검색 * tags: [Users] * parameters: * name: String * kcal: int * responses: * 200: * description: 성공 * content: * application/json: * schema: * type: array * items: * properties: * name: * type: String * example: 아메리카노 * kcal: * type: int * example: 5 */
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
[세션30] fetchUser시 UseGuard 401에러
안녕하세요, jwt토큰을 입력하여 사용자 인가를 받고 싶은데 그 과정에서 계속 오류가 나서 문의드립니다. 제가 확인하기로는 코드도 정확히 입력하고, HTTP HEADERS로 보내주는 토큰도 문제 없어서요. 에러 강의에서 jwt를 넣어주지 않았을 때 발생한다는 에러가 발생합니다. 제가 보기에는 토큰값이 잘못된것 같은데, login으로 받아온 토큰을 바로 넣는거라 만료가 되지도 않았을텐데 해결이 안되네요. 제 코드는 아래와 같습니다. users.resolver.ts// users.resolver.ts import { Args, Int, Mutation, Query, Resolver } from '@nestjs/graphql'; import { User } from './entities/user.entity'; import { UsersService } from './users.service'; import * as bcrypt from 'bcrypt'; import { UseGuards } from '@nestjs/common'; import { GqlAuthAccessGuard } from '../auth/guards/gql-auth.guard'; // import { AuthGuard } from '@nestjs/passport'; // 추가 @Resolver() export class UsersResolver { constructor( private readonly usersService: UsersService, // ) {} @UseGuards(GqlAuthAccessGuard) // 수정 @Query(() => String) fetchUser(): string { console.log('인가에 성공하였습니다'); return '인가에 성공하였습니다.'; } @Mutation(() => User) async createUser( @Args('email') email: string, @Args('password') password: string, @Args('name') name: string, @Args({ name: 'age', type: () => Int }) age: number, ): Promise<User> { // const hashedPassword = await bcrypt.hash(password, 10); // return this.usersService.create({ email, hashedPassword, name, age }); // } return this.usersService.create({ email, password, name, age }); } } gql-auth.guard.ts import { ExecutionContext } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; import { AuthGuard } from '@nestjs/passport'; export class GqlAuthAccessGuard extends AuthGuard('access') { getRequest(context: ExecutionContext) { const gqlContext = GqlExecutionContext.create(context); return gqlContext.getContext().req; } } jwt-access.strategy.ts import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: '나의비밀번호', }); } validate(payload) { console.log('페이로드', payload); return { id: payload.sub, }; } } auth.module.tsimport { Module } from '@nestjs/common'; import { AuthResolver } from './auth.resolver'; import { AuthService } from './auth.service'; import { UsersModule } from '../users/users.module'; import { JwtModule } from '@nestjs/jwt'; import { JwtAccessStrategy } from './strategy/jwt-access.strategy'; @Module({ imports: [ JwtModule.register({}), UsersModule, // 서비스를 각각 불러오기보다는 module을 통으로 불러오는게 낫다. 원래는 import 에 TypeOrmModule.forFeature([Users])& providers에 UsersServie가 있었음. ], providers: [ JwtAccessStrategy, AuthResolver, // AuthService, // ], }) export class AuthModule {}
-
미해결Do It! 장고+부트스트랩: 파이썬 웹개발의 정석
self.client.post에 글이 생성되지않습니다
강의 영상과 똑같이 작성했음에도 last_post의 내용이 client가 생성한 post가 아닌, post_003의 내용이 들어가면서 Failed가 발생합니다.실행결과 Failedself.assertEqual(last_post.title, 'Post Form 만들기')AssertionError: '세 번째 포스트 입니다.' != 'Post Form 만들기'- 세 번째 포스트 입니다.+ Post Form 만들기[test.py 코드]from time import sleep from django.test import TestCase, Client from bs4 import BeautifulSoup from .models import Post, Category, Tag from django.contrib.auth.models import User class TestView(TestCase): def setUp(self): self.client = Client() # 사용자 생성 self.user_yunju = User.objects.create_user( username='yunju', password='0129' ) self.user_subin = User.objects.create_user( username='subin', password='cute0313' ) self.user_yunju.is_staff = True self.user_yunju.save() # 카테고리 생성 self.category_programming = Category.objects.create( name='programming', slug='programming' ) self.category_music = Category.objects.create( name='music', slug='music' ) # 태그 생성 self.tag_python_kar = Tag.objects.create( name="파이썬 공부", slug='파이썬-공부' ) self.tag_python = Tag.objects.create( name="python", slug='python' ) self.tag_django = Tag.objects.create( name="django", slug='django' ) # 포스트 생성 self.post_001 = Post.objects.create( title="첫 번째 포스트 입니다.", content="Hello World! We are the World", author=self.user_yunju, category=self.category_programming ) self.post_001.tags.add(self.tag_django) self.post_002 = Post.objects.create( title="두 번째 포스트 입니다.", content="저는 마라탕과 떡볶이를 사랑합니다", author=self.user_subin, category=self.category_music ) self.post_003 = Post.objects.create( title="세 번째 포스트 입니다.", content="Category가 없는 포스트입니다.", author=self.user_subin ) self.post_003.tags.add(self.tag_django) self.post_003.tags.add(self.tag_python) # 내비게이션바 함수 def navbar_test(self, soup): navbar = soup.nav self.assertIn('Blog', navbar.text) self.assertIn('Blog', navbar.text) logo_btn = navbar.find('a', text='Do It Django') self.assertEqual(logo_btn.attrs['href'], '/') home_btn = navbar.find('a', text='Home') self.assertEqual(home_btn.attrs['href'], '/') blog_btn = navbar.find('a', text='Blog') self.assertEqual(blog_btn.attrs['href'], '/blog/') about_me_btn = navbar.find('a', text='About me') self.assertEqual(about_me_btn.attrs['href'], '/about_me/') # 카테고리 카드 함수 def category_card_test(self, soup): categories_card = soup.find('div', id='categories-card') self.assertIn('Categories', categories_card.text) self.assertIn( f'{self.category_programming}({self.category_programming.post_set.count()})', categories_card.text ) self.assertIn( f'{self.category_music}({self.category_music.post_set.count()})', categories_card.text ) self.assertIn( f'미분류({Post.objects.filter(category=None).count()})', categories_card.text ) # 포스트가 있는 경우 def test_post_list_with_posts(self): self.assertEqual(Post.objects.count(), 3) response = self.client.get('/blog/') self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.assertIn('Blog', soup.title.text) self.navbar_test(soup) self.category_card_test(soup) main_area = soup.find('div', id='main-area') post_001_card = main_area.find('div', id='post-1') self.assertIn(self.post_001.title, post_001_card.text) self.assertIn(self.post_001.category.name, post_001_card.text) self.assertIn(self.tag_django.name, post_001_card.text) self.assertNotIn(self.tag_python.name, post_001_card.text) self.assertNotIn(self.tag_python_kar.name, post_001_card.text) post_002_card = main_area.find('div', id='post-2') self.assertIn(self.post_002.title, post_002_card.text) self.assertIn(self.post_002.category.name, post_002_card.text) self.assertNotIn(self.tag_django.name, post_002_card.text) self.assertNotIn(self.tag_python.name, post_002_card.text) self.assertNotIn(self.tag_python_kar.name, post_002_card.text) post_003_card = main_area.find('div', id='post-3') self.assertIn(self.post_003.title, post_003_card.text) self.assertIn('미분류', post_003_card.text) self.assertIn(self.tag_django.name, post_003_card.text) self.assertIn(self.tag_python.name, post_003_card.text) self.assertNotIn(self.tag_python_kar.name, post_003_card.text) self.assertIn(self.post_001.author.username.upper(), main_area.text) self.assertIn(self.post_002.author.username.upper(), main_area.text) # 포스트가 없는 경우 def test_post_list_without_posts(self): Post.objects.all().delete() self.assertEqual(Post.objects.count(), 0) response = self.client.get('/blog/') self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.navbar_test(soup) self.assertIn('Blog', soup.title.text) main_area = soup.find('div', id='main-area') self.assertIn('아직 게시물이 없습니다.', main_area.text) # 상세페이지 함수 def test_post_detail(self): self.assertEqual(Post.objects.count(), 3) self.assertEqual(self.post_001.get_absolute_url(), '/blog/1/') response = self.client.get(self.post_001.get_absolute_url()) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.navbar_test(soup) self.category_card_test(soup) self.assertIn(self.post_001.title, soup.title.text) main_area = soup.find('div', id='main-area') post_area = main_area.find('div', id='post-area') self.assertIn(self.post_001.title, post_area.text) self.assertIn(self.post_001.category.name, post_area.text) self.assertIn(self.user_yunju.username.upper(), post_area.text) self.assertIn(self.post_001.content, post_area.text) self.assertIn(self.tag_django.name, post_area.text) self.assertNotIn(self.tag_python.name, post_area.text) self.assertNotIn(self.tag_python_kar.name, post_area.text) # 카테고리별 페이지 나타내는 함수 def test_category_page(self): response = self.client.get( self.category_programming.get_absolute_url()) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.navbar_test(soup) self.category_card_test(soup) main_area = soup.find('div', id='main-area') self.assertIn(self.category_programming.name, main_area.h1.text) self.assertIn(self.category_programming.name, main_area.text) self.assertIn(self.post_001.title, main_area.text) self.assertNotIn(self.post_002.title, main_area.text) self.assertNotIn(self.post_003.title, main_area.text) # 태그 페이지를 나타내는 함수 def test_tag_page(self): response = self.client.get(self.tag_django.get_absolute_url()) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.navbar_test(soup) self.category_card_test(soup) self.assertIn(self.tag_django.name, soup.h1.text) main_area = soup.find('div', id='main-area') self.assertIn(self.tag_django.name, main_area.text) self.assertIn(self.post_001.title, main_area.text) self.assertNotIn(self.post_002.title, main_area.text) self.assertIn(self.post_003.title, main_area.text) # 로그인하지 않은 사용자에 대한 폼 제한 함수 def test_create_post_without_login(self): response = self.client.get('/blog/create_post/') self.assertNotEqual(response.status_code, 200) # 폼(form)을 이용한 포스트 작성 페이지 생성 # 로그인한 사용자만 폼 작성 가능 def test_create_post_with_login(self): self.client.login(username='yunju', password='0129') response = self.client.get('/blog/create_post/') self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') self.assertEqual('Create Post - Blog', soup.title.text) main_area = soup.find('div', id='main-area') self.assertIn('Create a New Post', main_area.text) self.client.post( '/blog/create_post/', { 'title': 'Post Form 만들기', 'content': 'Post Form 페이지를 만듭시다.', }, ) last_post = Post.objects.last() self.assertEqual(last_post.title, 'Post Form 만들기') self.assertEqual(last_post.author.username, 'yunju') self.assertEqual(last_post.content, 'Post Form 페이지 만들어보자!')[views.py 코드]from django.core.exceptions import PermissionDenied from django.shortcuts import render, redirect from django.views.generic import ListView, DetailView, CreateView, UpdateView from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from .models import Post, Category, Tag # 리스트를 출력하는 django 기본 라이브러리 class PostList(ListView): model = Post ordering = '-pk' def get_context_data(self, **kwargs): context = super(PostList, self).get_context_data() context['categories'] = Category.objects.all() context['no_category_post_count'] = Post.objects.filter( category=None).count() return context # 하나의 리스트를 불러오는 django 기본 라이브러리 class PostDetail(DetailView): model = Post def get_context_data(self, **kwargs): context = super(PostDetail, self).get_context_data() context['categories'] = Category.objects.all() context['no_category_post_count'] = Post.objects.filter( category=None).count() return context # 포스트 생성 함수 class PostCreate(LoginRequiredMixin, UserPassesTestMixin, CreateView): model = Post fields = ['title', 'hook_text', 'content','head_image', 'file_upload', 'category'] def test_func(self): return self.request.user.is_superuser or self.request.user.is_staff def form_valid(self, form): current_user = self.request.user if current_user.is_authenticated and (current_user.is_staff or current_user.is_superuser): form.instance.author = current_user return super(PostCreate, self).form_valid(form) else: return redirect('/blog/') class PostUpdate(LoginRequiredMixin, UpdateView): model = Post fields = ['title', 'hook_text', 'content','head_image', 'file_upload', 'category'] template_name = 'blog/post_update_form.html' def dispatch(self, request, *args, **kwargs): if request.user.is_authenticated and request.user == self.get_object().author: return super(PostUpdate, self).dispatch(request, *args, **kwargs) else: return PermissionDenied # 카테고리별 페이지 반환 함수 def category_page(request, slug): if slug == 'no_category': category = '미분류' post_list = Post.objects.filter(category=None) else: category = Category.objects.get(slug=slug) post_list = Post.objects.filter(category=category) return render( request, 'blog/post_list.html', { 'post_list': post_list, 'categories': Category.objects.all(), 'no_category_post_count': Post.objects.filter(category=None).count(), 'category': category } ) # 태그별 페이지 반환 함수 def tag_page(request, slug): tag = Tag.objects.get(slug=slug) post_list = tag.post_set.all() return render( request, 'blog/post_list.html', { 'post_list': post_list, 'categories': Category.objects.all(), 'no_category_post_count': Post.objects.filter(category=None).count(), 'tag': tag } )
-
미해결Practical Testing: 실용적인 테스트 가이드
섹션7 - 테스트 환경의 독립성을 보장하자
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요. 강의 재미나게 잘 보고있습니다.제목에 남겨드린 강의에서 creatOrderWithNoStock의 주제와 벗어나 given절의 deductQuantity에서 예외가 발생할 수 있는 상황을 예를 들어서 설명해 주셨고, 테스트 환경에서는 가능한 생성자를 기반으로 환경을 구성하여 진행하는것이 좋다고 말씀 주셨는데 그럼 이 경우에는 Stock stock1 = Stock.create("001", 2); Stock stock2 = Stock.create("002", 1);와 같이 생성 시점에 이미 부족한 수량으로 설정 하여 테스트를 진행하여 deductQuantity를 사용하지 않고, deductQuantity의 경우 따로 단위 테스트를 통해 수량 감소를 확인하는걸로 테스트를 구성하는 식으로 가면 되는건지 궁금합니다.