묻고 답해요
169만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Node.js 노드 빠르게 훑어보기: 서버부터 DB까지
오류 발생 도움 필요
강의제목: Node.js 노드 빠르게 훑어보기: 서버부터 DB까지따라 하다 문제가 발생한 회차: 13강 화면 구성 - Template Engine 확장npm install nunjucks를 하고..강의에서 하라는 대로 하면서 views 폴더에서 base.html을 수정하고, write.html을 작성하고base.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 공통된 요소 --> <nav> <a href="">Logo</a> <a href="">글 작성</a> </nav> <!-- 바뀌는 요소 --> {% block content %} {% endblock %} <footer> <p>Footer</p> </footer> </body> </html>write.html{% extends 'base.html' %} {% block content %} <h1>글 작성 페이지입니다.</h1> {% endblock %}index.js도 수정하고 npm run dev 를 해보면..index.jsimport express from 'express'; import path from 'path'; import nunjucks from 'nunjucks'; const __dirname = path.resolve(); const app = express(); //view engine set app.set('view engine', 'html');//main.html->main(.html) //nunjucks nunjucks.configure('views', { watch: true, //html파일이 수정될 경우, 다시 반영 후 렌더링 express: app }) //middleware //main page GET app.get('/', (req, res)=>{ res.sendFile(__dirname + '/public/main.html'); }); app.get('/write', (req, res) => { res.render('write.html'); }) app.listen(3000, ()=> { console.log('Server is running'); });127.0.0.1:3000에서는 main page라고 그냥 나오지만127.0.0.1:3000/write를 실행해보면 아래와 같은 에러메시지가 나옵니다.혹시 해결책을 아시는 분은 답변 부탁드립니다.사실 첨부파일 zip으로 올릴 수 있으면 한꺼번에 올리고 싶은데링크 첨부밖에 없어서 일단 이렇게 올립니다.이거 질문란을 못 찾아서 일반 커뮤니티에 올렸다가 강의 페이지 들어오니 질문란이 있는 거 같아서 백업하여 여기다 다시 올립니다.Error: template not found: write.html at createTemplate (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\environment.js:234:15) at next (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\lib.js:260:7) at handle (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\environment.js:267:11) at C:\Users\SBA\user\node_study\my_app\node_modules\nunjucks\src\environment.js:276:9 at next (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\lib.js:258:7) at Object.asyncIter (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\lib.js:263:3) at Environment.getTemplate (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\environment.js:259:9) at Environment.render (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\environment.js:295:10) at NunjucksView.render (C:\Users\user\node\node_study\my_app\node_modules\nunjucks\src\express-app.js:18:9) at tryRender (C:\Users\user\node\node_study\my_app\node_modules\express\lib\application.js:657:10)
-
해결됨React + GPT API로 AI회고록 서비스 개발 (원데이 클래스)
왜 {import.meta.env.VITE_SOME_KEY} 가 적용이 안될까요 ㅠㅠ
/src/.env 파일에 VITE_SOME_KEY = 123이렇게 설정하고, App.jsx에 function App() { return(<>{import.meta.env.VITE_SOME_KEY} <Counter/> </> )}export default App;이렇게 설정했습니다. 근데 왜 화면엔 123이 출력이 안되는 걸 까요 ㅠ? 오류 메시지도 없고..강의랑 똑같이 했는데 왜 안나올끼요 ..
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
홈 화면 화살표 버튼 눌렀을때 1월에서 2월로 안 가고 3월로 갑니다 나머지 달은 잘 작동합니다.
안녕하세요 또 이렇게 질문을 해서 죄송합니다. 마지막 강의까지 다 듣고 배포까지 했는데 갑자기 홈 화면에 오른 화살표를 클릭을 하면 지금 1월 인데 누르면 3월로 이동 됩니다. 그런데 1월에서 2월 넘어갈때만 그래요 뒤로 가는건 잘 작동합니다. 홈 강의 다시 보고 틀린거 있나 확인했는데 못 찾아서 이렇게 연락드려요 https://github.com/jeain/Diary
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
SPA, MPA, 리액트 Hooks 등의 개념 어디서 참고하시나요?
강사님 SPA, MPA, SSR , CSR과 리액트 Hooks 등의 개념에 대해 찾다보면 참고 문서와 링크 없고, 내용에 틀린부분도 있어보이는 블로그가 종종 있더라구요. 이러한 경우 강사님은 어디서 주로 찾으시는지 궁금합니다.
-
해결됨React + GPT API로 AI회고록 서비스 개발 (원데이 클래스)
마크다운 이미지가 잘 작동안합니다.
제시해주신 방식대로는 마크다운으로 작성하는 이미지가 링크를 작성하기까지만 하고 나오지 않는 경우가 대부분입니다. 이유는 모르겠네요. 잠깐 나왔었는데.. 또 안나옵니다. 이유가 있을까요. 조건과 지시를 다양하게 걸었더니 제대로 답변을 못하네요. ^^; 특히 일기를 제멋대로 쓰는 부분이 있습니다. 어떤때는 내가쓴것처럼 잘 쓰는데 어떤때는 제멋대로 씁니다. ㅎㅎ 그래도 이런 시도를 해볼수 있어서 좋습니다.
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
React Router가 설치가 안됩니다.
npm i react-router-dom@6 로 설치하려하면 npm WARN EBADENGINE Unsupported engine {npm WARN EBADENGINE package: 'lru-cache@10.1.0',npm WARN EBADENGINE required: { node: '14 || >=16.14' },npm WARN EBADENGINE current: { node: 'v16.13.0', npm: '8.1.0' }npm WARN EBADENGINE } 라 뜨는데요, 현재 노드버전이 v16.13 이라 그런것같은데이럴때는 어떻게 해야되나요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
nginx 후 Front(502 Bad Gateway), back(welcome to nginx) 라고만 나오는 문제
안녕하세요 선생님 front, back nginx 한 뒤로둘다 https라고 바뀌고 인증서도 있긴한데,Front(502 Bad Gateway)라고 나오고back(welcome to nginx) 라고만 나오는 상태입니다.(설치는 Nginx Ubuntu20보고 했습니다https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal&tab=standard)문제1)그래서 첫번째 문제로 back에서 sudo npx pm2 logs --err --lines 200를 해보았을 땐 아래와 같은 경고가 나왔습니다.0|app | Warning: connect.session() MemoryStore is not 0|app | designed for a production environment, as it will leak 0|app | memory, and will not scale past a single process.질문1)찾아보니까 express-session 미들웨어의 기본 메모리 저장소(MemoryStore)를 사용할 때 MemoryStore가 개발 환경에서는 적합하지만, 실제 프로덕션 환경에서는 메모리 누수 문제와 단일 프로세스 제한으로 인해 적합하지 않아 프로덕션 환경에서는 Redis, MongoDB 등의 세션 저장소를 사용하라는데, 그럼 front 화면이 나오는건지 궁금합니다,, 문제2)그리고 두번째 문제로 back에서 tail /var/log/nginx/error.log를 했을 땐 아래와 같은 에러가 나왔습니다. ubuntu@ip-172-31-12-59:~/react_nodebird/back$ tail /var/log/nginx/error.log 2024/01/24 12:19:54 [warn] 420260#420260: conflicting server name "api.luckyhaejin.com" on 0.0.0.0:80, ignored 2024/01/24 12:19:54 [notice] 420260#420260: signal process started질문2)찾아보니 Nginx 설정 파일 내에서 api.luckyhaejin.com이라는 서버 이름(server name)이 80 포트에서 두 번 이상 선언되었음을 나타내는 에러라는데 어떤 부분이 잘못되었는지 잘 모르겠어서 어딜 확인하면 좋을지 문의 드립니다. 질문3)강의에서 Ubuntu서버만 바꿔주고 로컬은 바뀌는 부분 이없는거같아서 Ubuntu서버에서만 바꿔줬는데, 그럼 로컬에도 Ubuntu에 설치한 것 다 포함해서 코드까지 다 바꿔준 뒤 Ubuntu에서 git pull 다시 해줘야할까요,,? 현재 설정된 내용)front=> /etc/nginx/nginx.conf => server관련(글을 옮겨적으니까 들여쓰기 해서 정리 한게 코드가 전부 합쳐져서 사진으로 올립니닷,,)front/pacakage.json에서 start부분에 3060 잘 되어있음front => /etc/nginx/nginx.conf front/config/config.js에서 backUrl설정 잘 되어있음back => /etc/nginx/nginx.confback => app.js(사진이 보기 편하실거같아서 코드랑 둘다올려욧)const express = require('express'); const cors = require('cors'); const session = require('express-session'); const cookieParser = require('cookie-parser'); const passport = require('passport'); const dotenv = require('dotenv'); const morgan = require('morgan'); const postRouter = require('./routes/post'); const postsRouter = require('./routes/posts'); const userRouter = require('./routes/user'); const hashtagRouter = require('./routes/hashtag'); const db = require('./models'); const passportConfig = require('./passport'); const path = require('path'); const hpp = require('hpp'); const helmet = require('helmet'); dotenv.config(); const app = express(); db.sequelize.sync() .then(() => { console.log('DB 연결 성공'); }).catch(console.error); passportConfig(); if(process.env.NODE_ENV === 'production'){ app.use(morgan('combined')); app.use(hpp()); app.use(helmet()); app.use(cors({ origin: 'https://luckyhaejin.com', credentials: true })); } else { app.use(morgan('dev')); } app.use('/', express.static(path.join(__dirname, 'uploads'))); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use(cookieParser(process.env.COOKIE_SECRET)); app.use(session({ saveUninitialized: false, resave: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, //자바스크립트로 접근하지못하게 secure: true, //일단 false로 하고 https적용할 땐 ture domain: process.env.NODE_ENV = 'production' && '.luckyhaejin.com' //도메인 사용할 경우 }, })); app.use(passport.initialize()); app.use(passport.session()); app.get('/', (req, res) =>{ res.send('hello express'); }); app.use('/posts', postsRouter); app.use('/post', postRouter); app.use('/user', userRouter); app.use('/hashtag', hashtagRouter); app.listen(3065, () => { console.log('서버 실행 중'); }); back => /etc/nginx/nginx.conf사용중인 Os) macOS
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
에뮬레이터에 화면 흰색만나오는 문제
import { API_URL } from "./config/constants.js"; import avatarImg from "./assets/icons/avatar.png"; import React from "react"; import { StyleSheet, Text, View, Image, ScrollView, Dimensions, TouchableOpacity, Alert, } from "react-native"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import Carousel from "react-native-reanimated-carousel"; import axios from "axios"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; import "dayjs/locale/ko"; dayjs.extend(relativeTime); dayjs.locale("ko"); export default function App() { const [products, setProducts] = React.useState([]); const [banners, setBanners] = React.useState([]); React.useEffect(() => { axios .get(`${API_URL}/products`) .then((result) => { const products = result.data.products; setProducts(products); }) .catch((error) => { console.log("error :", error); }); axios .get(`${API_URL}/banners`) .then((result) => { const banners = result.data.banners; setBanners(banners); }) .catch((error) => { console.log("error :", error); }); }, []); return ( <GestureHandlerRootView> <View style={styles.container}> <ScrollView> <Carousel data={banners} width={Dimensions.get("window").width} height={200} autoPlay={true} sliderWidth={Dimensions.get("window").width} itemWidth={Dimensions.get("window").width} itemHeight={200} renderItem={(obj) => { return ( <TouchableOpacity onPress={() => { Alert.alert("배너 클릭"); }} > <Image style={styles.bannerImage} source={{ uri: `${API_URL}/${obj.item.imageUrl}` }} resizeMode="contain" /> </TouchableOpacity> ); }} /> <Text style={styles.headline}>판매되는 상품들!</Text> <View style={styles.productList}> {products.map((product, index) => { return ( <View key={index} style={styles.productCard}> {product.soldout === 1 && <View style={styles.productBlur} />} <View> <Image style={styles.productImg} source={{ uri: `${API_URL}/${product.img_url}`, }} resizeMode={"contain"} /> </View> <View style={styles.productContents}> <Text style={styles.productName}>{product.name}</Text> <Text style={styles.productPrice}>{product.price}원</Text> <View style={styles.productFooter}> <View style={styles.productSeller}> <Image style={styles.productAvatar} source={avatarImg} /> <Text style={styles.productSellerName}> {product.seller} </Text> </View> <Text style={styles.productDate}> {dayjs(product.created_at).fromNow()} </Text> </View> </View> </View> ); })} </View> </ScrollView> </View> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ headline: { fontSize: 24, fontWeight: "800", marginTop: 10, marginBottom: 10, }, container: { flex: 1, backgroundColor: "#fff", paddingTop: 32, margin: 10, }, productCard: { width: "100%", borderColor: "rgb(230,230,230)", borderWidth: 1, borderRadius: 16, backgroundColor: "white", marginBottom: 10, }, productBlur: { position: "absolute", top: 0, bottom: 0, right: 0, left: 0, backgroundColor: "#ffffffaa", zIndex: 999, }, productImg: { width: "100%", height: 210, }, productContents: { padding: 8, }, productSeller: { flexDirection: "row", }, productAvatar: { width: 24, height: 24, }, productFooter: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginTop: 12, }, productName: { fontSize: 14, }, productPrice: { fontSize: 16, fontWeight: "600", marginTop: 8, }, productSellerName: { fontSize: 14, }, productDate: { fontSize: 14, }, productList: { alignItems: "center", }, bannerImage: { width: "100%", height: 200, }, }); 어떤 오류메세지도 뜨지않고,에뮬레이터에 화면이 출력되지않는 문제가 발생합니다.Carousel을 적용하기전에는 화면 잘 출력되었는데,Carousel을 적용하니 화면이 출력되지않네요..Error: PanGestureHandler must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/installation for more details.이러한 오류가 발생해서GestureHandlerRootView 태그로 최상단에 묶어주니 저 오류는 사라졌는데,애뮬레이터의 화면이 출력되지 않는 문제가 발생합니다.서버는 잘 연결되어있는걸 확인햇습니다..뭐가문제일까요
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
Counter 컴포넌트가 2번씩 호출되는 이유
안녕하세요, Counter 컴포넌트 호출 회수를 디버깅 하기 위해 console.log("counter 호출!"); 구문을 아래와 같이 추가해주었는데, 강사님과는 다르게 저는 처음 렌더링 될 때와 count의 상태 값이 변화할 때 마다 counter 호출이 두 번씩 일어납니다. 이유가 무엇일까요..? 아무리 생각해봐도 이유를 모르겠습니다. 위 브라우저 콘솔 사진은 최초 렌더링 되고나서 개발자도구를 켰을 때 모습입니다. 처음부터 두 번이 호출되어 있고, 그 이후에도 count의 상태를 변화시킬 때 마다 두 번씩 로그에 찍힙니다.
-
해결됨React + GPT API로 AI회고록 서비스 개발 (원데이 클래스)
프롬프트 명령어 - 입력값과 동일한 언어로 받으려면 어떻게 작성할까요?
좋은 내용 감사합니다.프롬프트 내용중에 "Translate Into Korean~" 이라는 내용으로 답변을 한글로 받게 됩니다.혹시 [events] 밑에 오는 사용자 입력값과 동일한 언어로 결과를 받고 싶다면 어떻게 작성하면 될까요?강의 내용을 기준으로 다국어 서비스를 만들려고 하는데, 영어가 짧아서 질문 드려요
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기[전체 리뉴얼]
제품을 배열로 다시 재정리 해줄때 split 후에 map을 하는 이유
따라하며-배우는-노드-리액트-쇼핑몰 > 쇼핑 카트 페이지 데이터 가져오기 강의를 수강중에 있습니다.17:30 부근에 제품을 배열로 다시 재정리 해주는 걸 설명해주고 계시는데 split 후에 map을 하는 이유가 궁금합니다.값을 map 처리하기 전후로 찍어보니 값이 동일하게 나오는데 map 처리를 해주는 이유가 따로 있나 싶어서 여쭤봅니다.
-
해결됨클론코딩에서 알려주지 않는 것들 (보안, DDD, 마이크로서비스) 2편
몇가지 질문
안녕하세요. 강의 잘 보았습니다.몇가지 궁금증이 생겼는데요. Aggregate간의 의존성과 결합도를 해소하기 위해보통의 DDD 예제에서 Event를 사용하는 법을 많이 알려주고 있는데요. 이 방식을 채택하기에 모종의 이유로 실무에서 적용하지 못한다고 하였을때는 어떤식으로 의존성을 제거 또는 낮출수 있는지 궁금합니다. 두번째로는회원 도메인에서의 회원과 주문 도메인의 회원, 그리고 배송 도메인에서의 회원이 각 도메인별로 갖는 의미와 역할/기능 이 다를것 같은데요.예를 들어주문 context에서 회원에게 특정 기능이 필요하다고 가정했을때 이 코드가 어떤 entity에 작성이 되어야 하는지 궁금합니다.기존의 user entity에 코드를 작성해야 되는지아니면 order context내에 user entity를 새로 만들어서 코드 작성해야 되는지 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
git hub 업로드가 안되서 여쭤봐요
저같은경우 윈도우를 사용하고 있는데 두가지경우가 나오질 않아서 깃허브에 확인해보니 이렇게 프리캠프는 생성이 되었는데 들어가보니 강의에서 본것처럼 파일이 업로드 되있질 않았어요..어떻게 해야할까요?
-
미해결습관부터 바꿔주는 Node.js & Express 기초
포매팅
강의하시면서 자동으로 띄어쓰기같은 포매팅이 되던데단축키인지 아니면 익스텐션인지 알 수 있을까용?
-
해결됨
예외처리가 더 효율적인 코드
유저 로그인 관련 메서드를 구현하던 중, 예외처리 방법 두가지 중 어느 것이 더 효율적(가독성, 유지보수 측면 등등..)인지 여쭤보고 싶습니다첫번째 방법: try문에 NotFoundException을 던지고, catch문에서 instanceof를 사용해서 예외의 타입을 확인하고 처리하기async userLogin(nickname: string, password: string) { try { const user = await this.usersRepository.findOne({ where: { nickname } }); // console.log(user) if (user && (await bcrypt.compare(password, user.password))) { return user; } else { throw new NotFoundException('아이디 또는 비밀번호가 일치하지 않습니다.'); } } catch (e) { console.error(e); if (e instanceof NotFoundException) { throw e; // NotFoundException은 그대로 던지기 } else { throw new InternalServerErrorException('알 수 없는 오류'); } } } 두번째 방법: try문에서는 일반적인 Error객체를 던진 후 catch문에서 error.message를 확인하여 예외 유형을 판단하기 async userLogin(nickname: string, password: string) { try { const user = await this.usersRepository.findOne({ where: { nickname } }); // console.log(user) if (user && (await bcrypt.compare(password, user.password))) { return user; } else { throw new Error('아이디 또는 비밀번호가 일치하지 않습니다.'); } } catch (error) { if (error.message === '아이디 또는 비밀번호가 일치하지 않습니다.') { throw new NotFoundException('아이디 또는 비밀번호가 일치하지 않습니다.'); } else { // 다른 예외 처리 throw new InternalServerErrorException('알 수 없는 오류'); } } }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
AccessToken은 잘 만들어지는데 payload에 아무것도 안담겨요
로그인 시 받은 accesstoken을 밑에 /user/test의 헤더에 넣었습니다.처음에는 payload에 값이 담겼는데 갑자기 아무것도 안담기네요ㅜㅜ변경사항이 있었던 건 다 돌려봤는데도 도저히 뭐가 문제인지 모르겠어서 남깁니다.뭐가 문제인지도 모르겠어서 코드도 뭘 보여드려야 할 지 모르겠네요ㅜㅜ accesstoekn은 잘 만들어지는데.. 저 accesstoekn으로 UseGuards(AuthGuard('access')) 이 가드를 통과하는 거 아닌가요?validate()까지 간 거 보면 인가는 됐다고 생각했는데 왜 payload에 아무것도 안담기는지 모르겠네요ㅜㅜ
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
diaryList.map in not function 오류
안녕하세요.데이터 추가 기능 구현 중 오류가 생겼는데, 여러번 들어봐도 어디서 놓쳤는지 알수 없어서 문의드립니다.저장 성공 얼럿이 뜬 후 diaryList.map is not a function 오류가 발생하는데 이유가 뭘까요ㅠㅠ? 올려주신 코드와 비교해봐도 잘못 작성된 부분을 찾지 못해서.. 확인 부탁드립니다!https://codesandbox.io/p/sandbox/dawn-silence-dtzj4w?file=%2Fsrc%2FApp.js%3A5%2C1 감사합니다.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
#20 리액트 라우터 돔
라우터 돔 사이트에서 베이직이 안보이는데 어떤걸 복사하면되는건가요?
-
해결됨클론코딩에서 알려주지 않는 것들 (보안, DDD, 마이크로서비스) 2편
review write관련
안녕하세요. 질문이 많네요. review를 저장하는 코드를 보았습니다..product를 db에서 읽고,해당 product관련된 모든 review를 읽고,새로운 review를 추가하고,transaction을 걸고, product 저장하고, review들을 다시 저장하도록 하는 구조로 보입니다.어떤 의도인지는 이해는 갑니다. 궁금한점은review만 추가하는데도 기존 product를 저장하려고 시도하는건가요?만약 기존리뷰들의 저장은 conflict되면 무시하는건가요?리뷰가 보통 수백 수천건 되는 경우가 많을 텐데 그런건 고려가 안된것일까요? 확인 부탁드립니다.감사합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
테이블 관계
안녕하세요예를 들어 MockUserRepository를 만들고 그 안에 mydb라는 데이터가 있습니다.만약 기존 User 테이블이 Post 테이블과 관계가 맺어져 있다면 Post까지 mydb에 담아야 하나요?mydb = [{ id= '', name:='', password= '' }] 이렇게 해야 하는 건지아니면 mydb = [{ id: '', name:'', password: '', post: { id: '', title: ''}] 이렇게 작성해야 하는 건지 궁금합니다!! 그리고 userService에서 postService를 사용하는 로직이 있는데 userService 테스트시 MockPost 테이블까지 같이 구현해야 하는 건지..아니면 다른 방법이 있는 건지ㅜㅜ 궁금하네요 하나만 더 여쭤보자면.. ㅜㅜ 제가 이해가 잘 안가서 그러는데 controller.ts를 테스트할 때는 service를 mock하고, service.ts를 테스트할 때는 데이터베이스를 mock해서 사용하는 건가요??