묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결애플 웹사이트 인터랙션 클론!
페이지가 처음 로딩 되었을 때 애니메이션 처리가 되지 않는 느낌입니다
페이지가 맨 처음 로딩 되었을 때에는 애니메이션이 작동하지 않다가... 페이지 섹션을 일정 부분 진행하고 다시 윗방향 스크롤을 걸었을때 애니메이션이 정상작동을 합니다.그 후부터는 페이지의 시작 위치로 이동해서 시작부터 차근차근 내렸을때 정상작동을 합니다.원인이 잘 생각이 나지 않네요 Main.js(() => { let yOffset = 0; // window.pageYOffset 대신에 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치(yOffset) 보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; // 현재 활성화된 (눈 앞에 보고있는) 씬 (Scrollsection) let enterNewScene = false; // 새로운 Scene이 시작된 순간 true // sceneInfo는 각 scene에 대한 정보를 담고 있다. const sceneInfo = [ { // 0 type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-0'), messageA: document.querySelector('#scroll-section-0 .main-message.a'), messageB: document.querySelector('#scroll-section-0 .main-message.b'), messageC: document.querySelector('#scroll-section-0 .main-message.c'), messageD: document.querySelector('#scroll-section-0 .main-message.d') }, values: { messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }], messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }], messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }], messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }], messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }], messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }], messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }], messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }], messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }] } }, { // 1 type: 'normal', // heightNum: 5, // type normal에서는 필요 없음 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), content: document.querySelector('#scroll-section-1 .description') } }, { // 2 type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-2'), messageA: document.querySelector('#scroll-section-2 .a'), messageB: document.querySelector('#scroll-section-2 .b'), messageC: document.querySelector('#scroll-section-2 .c'), pinB: document.querySelector('#scroll-section-2 .b .pin'), pinC: document.querySelector('#scroll-section-2 .c .pin') }, values: { messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }], messageB_translateY_in: [30, 0, { start: 0.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }] } }, { // 3 type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3'), canvasCaption: document.querySelector('.canvas-caption') }, values: { } } ]; function setLayout() { // 각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { if(sceneInfo[i].type === 'sticky') { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; // window.innerHeight는 브라우저 창의 높이 } else if(sceneInfo[i].type === 'normal') { sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight } sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px` } yOffset = window.scrollY; let totalScrollHeight = 0; for (let i = 0; i < sceneInfo.length; i++) { totalScrollHeight += sceneInfo[i].scrollHeight; if(totalScrollHeight >= yOffset) { // 이미지로 그려보면 이해가 쉬움 currentScene = i; break; } } document.body.setAttribute('id', `show-scene=${currentScene}`) } function calcValues (values, currentYOffset) { // currentYOffset은 현재 씬에서 얼마나 스크롤 됐는지를 의미 let rv; // 현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기 const scrollHeight = sceneInfo[currentScene].scrollHeight const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; if (values.length === 3) { // start ~ end 사이의 애니메이션을 실행 const partScrollStart = values[2].start * scrollHeight; const partScrollEnd = values[2].end * scrollHeight; const partScrollHeight = partScrollEnd - partScrollStart if (currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd) { rv = (currentYOffset - partScrollStart) / partScrollStart * (values[1] - values[0]) + values[0]; } else if (currentYOffset < partScrollStart) { rv = values[0]; } else if (currentYOffset > partScrollEnd) { rv = values[1]; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } return rv; } function playAnimation() { const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; switch (currentScene) { case 0: // console.log('0 play'); if (scrollRatio <= 0.22) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`; } break; case 2: // console.log('2 play'); if (scrollRatio <= 0.32) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.67) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } if (scrollRatio <= 0.93) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } break; case 3: // console.log('3 play'); break; } } function scrollLoop() { enterNewScene = false; prevScrollHeight = 0; // PrevScrollHeight의 초기화가 함수 내에 있어야 맥북 기준 4 scene의 총합 11540이 찍히고 다음 scrollLoop() 이 실행될 때 다시 초기화 된다. (값이 누적되지 않는다.) for (let i = 0; i < currentScene; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); } if(yOffset < prevScrollHeight) { enterNewScene = true; if (currentScene === 0) return; // scene 0에서 바운싱 스크롤이 일어났을 때 콘솔에 -값을 찍히지 않게 방지 currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.scrollY; // window.pageYOffset은 deprecated 되었다. scrollLoop() }); // window.addEventListener('DOMContentLoaded', setLayout) window.addEventListener('load', setLayout) window.addEventListener('resize', setLayout) // 화면 크기가 바뀌었을때를 기준으로 setLayout을 정의하기 })();main.css@charset "utf-8"; html { font-family: 'Noto Sans KR' , sans-serif; font-size: 14px; } body { overflow-x: hidden; color: rgb(29, 29, 31); letter-spacing: -0.05em; background: white; } p { line-height: 1.6; } a { color: rgb(29, 29, 31); text-decoration: none; } .global-nav { position: absolute; top: 0; left: 0; width: 100%; height: 44px; padding: 0 1rem; } .local-nav { position: absolute; top: 45px; left: 0; width: 100%; height: 52px; padding: 0 1rem; border-bottom: 1px solid #ddd; } .global-nav-links, .local-nav-links { display: flex; align-items: center; max-width: 1000px; height: 100%; margin: 0 auto; /* background-color: gold; */ } .global-nav-links { justify-content: space-between; } .local-nav-links .product-name { margin-right: auto; font-size: 1.4rem; font-weight: bold; } .local-nav-links a { font-size: 0.8rem; } .local-nav-links a:not(.product-name) { margin-left: 2em; } .scroll-section { border: 3px solid red; padding-top: 50vh; } #scroll-section-0 h1 { font-size: 4rem; text-align: center; } .main-message { display: flex; align-items: center; justify-content: center; top: 35vh; margin: 5px 0; height: 3em; font-size: 2.5rem; opacity: 0; } .main-message p { font-weight: bold; text-align: center; line-height: 1.2; } .main-message small { display: block; margin-bottom: 0.5em; font-size: 1.2rem; } #scroll-section-2 .main-message { font-size: 3.5rem; } .description { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 1.2rem; color: #888 } .description strong { float: left; margin-right: 0.2em; /* font-size: 3rem; */ color: rgb(29, 29, 31); } .desc-message { width: 50%; font-weight: bold; } .pin { width: 1px; height: 100px; background: rgb(29,29,31); } #scroll-section-2 .b { top: 10%; left: 40%; } #scroll-section-2 .c { top: 15%; left: 45%; } .mid-message { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 2rem; color: #888; } .mid-message strong { color: rgb(29,29,31); } .canvas-caption { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 1.2rem; color: #888; } .footer { display: flex; align-items: center; justify-content: center; height: 7rem; color: white; background: darkorange; } .sticky-elem { display: none; position: fixed; left: 0; width: 100%; } body#show-scene-0 #scroll-section-0 .sticky-elem, body#show-scene-1 #scroll-section-1 .sticky-elem, body#show-scene-2 #scroll-section-2 .sticky-elem, body#show-scene-3 #scroll-section-3 .sticky-elem { display: block; } @media(min-width: 1024px) { #scroll-section-0 h1 { font-size: 9vw; } .main-message { font-size: 4vw; } .description { font-size: 2rem; } .description strong { font-size: 6rem; } #scroll-section-2 .main-message { font-size: 6vw; } .main-message small { font-size: 1.5vw; } .desc-message { width: 20%; } #scroll-section-2 .b { top: 20%; left: 53%; } #scroll-section-2 .c { left: 55%; } .mid-message { font-size: 4vw; } .canvas-caption { font-size: 2rem; } }blank.html<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>AirMug Pro</title> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="css/default.css"> <link rel="stylesheet" href="css/main.css"> </head> <body> <div class="container"> <nav class="global-nav"> <div class="global-nav-links"> <a href="" class="global-nav-item">Rooms</a> <a href="" class="global-nav-item">Ideas</a> <a href="" class="global-nav-item">Stores</a> <a href="" class="global-nav-item">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#" class="product-name">AirMug Pro</a> <a href="#">개요</a> <a href="#">제품사양</a> <a href="#">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMug Pro</h1> <div class="sticky-elem main-message a"> <p>온전히 빠져들게 하는<br>최고급 세라믹</p> </div> <div class="sticky-elem main-message b"> <p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p> </div> <div class="sticky-elem main-message c"> <p>온전히 편안한<br>맞춤형 손잡이</p> </div> <div class="sticky-elem main-message d"> <p>새롭게 입가를<br>찾아온 매혹</p> </div> </section> <section class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>보통 스크롤 영역</strong> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolore explicabo commodi facere nisi expedita eaque quasi doloremque eligendi natus architecto, facilis velit nobis voluptatum suscipit autem soluta debitis neque illum quibusdam similique odio maiores. Ab optio laborum eum sit praesentium, aperiam voluptatem nihil modi tempore a magni necessitatibus hic exercitationem temporibus doloremque error omnis. Sequi, repudiandae dolor eaque non soluta praesentium doloremque, doloribus, ea consequuntur sit perferendis ex! Repellendus natus harum soluta dolorem voluptas alias qui, ipsa vero! Alias voluptate libero, facilis molestias nam aspernatur, amet accusantium consequatur a nostrum temporibus. Qui obcaecati optio quod iusto totam, aut iste dolor ea. Explicabo dolorem ducimus natus, officiis aut minima, ad sapiente voluptatem repellat aliquid suscipit at. Architecto nostrum perferendis, fugiat velit perspiciatis illum dignissimos, vitae delectus magnam blanditiis omnis ratione nihil minus deserunt repudiandae necessitatibus et, tempore cupiditate sapiente? Debitis obcaecati repellendus corrupti odit incidunt deleniti exercitationem dolorum ipsum consequatur rerum maxime, corporis placeat deserunt veniam cum qui dolorem nulla fugit eius ipsam quibusdam saepe! Aut culpa excepturi perferendis, sint animi tempora. Officia necessitatibus libero impedit alias commodi excepturi accusamus molestiae modi laudantium voluptates totam earum autem eos numquam at consequuntur ipsam repellat, perspiciatis veniam? Expedita nobis ducimus quisquam accusantium! Velit! </p> </section> <section class="scroll-section" id="scroll-section-2"> <div class="sticky-elem main-message a"> <p> <small>편안한 촉감</small> 입과 하나 되다 </p> </div> <div class="sticky-elem desc-message b"> <p> 편안한 목넘김을 완성하는 디테일한 여러 구성 요소들, 우리는 이를 하나하나 새롭게 살피고 재구성하는 과정을 거쳐 새로운 수준의 머그, AirMug Pro를 만들었습니다. 입에 뭔가 댔다는 감각은 어느새 사라지고 오롯이 당신과 음료만 남게 되죠. </p> <div class="pin"></div> </div> <div class="sticky-elem desc-message c"> <p> 디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나 </p> <div class="pin"></div> </div> </section> <section class="scroll-section" id="scroll-section-3"> <p class="mid-message"> <strong>Retina 머그</strong> 아이디어를 광할하게 펼칠<br> 아름답고 부드러운 음료 공간. </p> <p class="canvas-caption"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita nulla nesciunt tempora asperiores culpa, illo et assumenda vel odio voluptatem? Saepe rerum qui accusamus maiores ex libero natus obcaecati accusantium aperiam eligendi ut et facere quis molestias quos repudiandae, omnis facilis deserunt cum! Expedita ipsam aliquid beatae doloremque, officia explicabo non neque reprehenderit qui quo, recusandae incidunt saepe totam! Adipisci dolorem inventore soluta. Natus pariatur sit quisquam dicta placeat! Molestias voluptas eos asperiores maxime, dolorem corporis eum quos iste dolores eveniet, cumque officiis beatae adipisci! Corporis quibusdam voluptates explicabo officiis quis cupiditate qui officia expedita. Praesentium placeat debitis recusandae ipsa similique optio accusamus, omnis vero a ducimus blanditiis fugit asperiores maiores mollitia? Pariatur exercitationem culpa, fugiat accusantium soluta incidunt beatae debitis laborum, harum neque aliquam, asperiores quod! Error numquam voluptate aut corrupti nam! Iusto illum accusamus sapiente asperiores quo eum perspiciatis aliquam blanditiis dolorem natus, modi magnam molestiae accusantium porro quisquam iure tempora laborum deserunt recusandae omnis dolore eos pariatur eveniet. Exercitationem mollitia inventore at quasi aperiam blanditiis minima accusantium, delectus, possimus quidem labore sapiente eius sint molestias doloribus est autem cupiditate iure veniam optio! Placeat dolor et, eos, saepe nemo tempore aliquid alias, facere pariatur optio sint nam ea. </p> </section> <footer class="footer"> 2023-08-01 Jun </footer> </div> <script src="js/main.js"></script> </body> </html>
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
몽구스 객체값 저장하기
cheerio로 가져온값을 객체로 만든다음 밖으로 보내서 최종적으로 몽구스로 저장하고싶은데문제가 생겼던게 저상태로 보내면 [object:object] 로 나오므로 JSON.stringfy()작업으로 문자열 형태로 내보내 주었습니다. 하지만 schema부분을 object로 하여도 object가 가장 상위의 데이터 타입으로 string형태로 저장된다고 여겨졌습니다. 그래서 JSON.stringfy()로 빼내고 저장하는 부분에서 JSON.parse로 하여 다시 저장할려 했지만 다시 [object:object]로 나와서 이부분을 어떻게 해결해야할지 잘찾아봐도 모르겠어서 질문 남겼습니다.// siteInfo.js import axios from "axios"; import cheerio from "cheerio"; export class SiteInfo { siteInfo = async (prefer) => { console.log(prefer); const siteData = await axios .get(prefer) .then() .catch((err) => { console.log("서버 데이터 안받아와짐 오류 : " + err); }); const keys = []; const values = []; const $ = cheerio.load(siteData.data); $("meta").each((index, el) => { if ( $(el).attr("property") && ($(el).attr("property").includes("og:title") || $(el).attr("property").includes("og:description") || $(el).attr("property").includes("og:image")) ) { const content = $(el).attr("content"); const title = $(el).attr("property"); keys.push(title); values.push(content); } }); const arr = []; for (let i = 0; i < keys.length; i++) { arr.push({ [keys[i]]: values[i], }); } console.log("여기 테스트 : " + arr); return arr; }; } // signup.controller.js export class SignupController { siteInfoService; isValueService; saveDataService; emailService; constructor(siteInfoService, isValueService, saveDataService, emailService) { this.siteInfoService = siteInfoService; this.isValueService = isValueService; this.saveDataService = saveDataService; this.emailService = emailService; } singUp = async (req, res) => { const name = req.body.name; const email = req.body.email; const personal = req.body.personal; const prefer = req.body.prefer; const pwd = req.body.pwd; const phone = req.body.phone; console.log(req.body); const hasValue = this.isValueService.checkToken(phone); if (hasValue === false) { res.json("isAuth값이 false입니다"); } const siteInfo = await this.siteInfoService.siteInfo(prefer); const ttt = JSON.parse(siteInfo); console.log("여기 문제 발생 : " + typeof ttt); console.dir("여기 문제 발생 22222: " + ttt); const dataSave = await this.saveDataService.dataSave({ name, email, personal, prefer, pwd, phone, siteInfo, }); const isValue = this.emailService.checkMail(email); if (isValue === false) { res.send("이메일 형식이 잘못되었습니다"); } const template = this.emailService.welcomeMessage({ name, phone, prefer }); this.emailService.sendTemplateToEmail({ template, email, }); res.send("_id : " + dataSave); }; userSearch = async (req, res) => { const searchReault = await this.saveDataService.dataSearch(); console.log(searchReault); res.send(searchReault); }; } // index.js import express from "express"; import mongoose from "mongoose"; import cors from "cors"; // import { Token, User } from "./mvc/model/schema.model.js"; import { TokenController } from "./mvc/controller/token.controller.js"; import { SignupController } from "./mvc/controller/signup.controller.js"; // 사이트 정보 가져오기 import { SiteInfo } from "./mvc/controller/service/siteInfo.js"; // 핸드폰 정보 맞는지 boolean으로 import { CheckToken } from "./mvc/controller/service/token.js"; // db저장하고 리턴값 가져오기 import { UserDb } from "./mvc/controller/service/userDb.js"; // 이메일 확인하고 보내주기 import { EmailService } from "./mvc/controller/service/email.js"; // 핸드폰 문자 보내기 import { PhoneService } from "./mvc/controller/service/phone.js"; const app = express(); const corsOptions = { origin: "http://127.0.0.1:5500", }; app.use(cors(corsOptions)); const siteInfo = new SiteInfo(); const checkToken = new CheckToken(); const userDb = new UserDb(); const emailService = new EmailService(); const phoneService = new PhoneService(); app.use(express.json()); const signUpController = new SignupController( siteInfo, checkToken, userDb, emailService ); app.post("/users", signUpController.singUp); app.get("/users", signUpController.userSearch); const tokenController = new TokenController(phoneService, checkToken); app.post("/tokens/phone", tokenController.insertdata); app.patch("/tokens/phone", tokenController.checkToken); mongoose.set("debug", true); mongoose .connect("mongodb://mongodb-file:27017/dockerconnetor") .then(() => { console.log("connect success"); }) .catch(() => { console.log("fail to connect with db"); }); app.listen(3001, () => { console.log("server open"); }); // userDb.js import { User } from "../../model/schema.model.js"; export class UserDb { dataSave = async ({ name, email, personal, prefer, pwd, phone, siteInfo, }) => { const siteInforesult = JSON.parse(siteInfo); console.log("여기 확인 : " + siteInforesult); const data = new User({ name: name, email: email, personal: personal, prefer: prefer, pwd: pwd, phone: phone, siteInfo: siteInfo, }); await data.save(); const idData = await User.findOne({ phone: phone }, { _id: 1 }).exec(); return idData._id; }; dataSearch = async (name) => { console.log("start datasearch"); const result = await User.find().exec(); return result; }; } // schema.model.js import mongoose from "mongoose"; const tokenSchema = new mongoose.Schema({ phone: String, token: Number, isAuth: Boolean, }); export const Token = mongoose.model("Token", tokenSchema); const userSchema = new mongoose.Schema({ name: String, email: String, personal: String, prefer: String, pwd: String, phone: String, siteInfo: { title: String, content: String, image: String, }, }); export const User = mongoose.model("User", userSchema);
-
미해결자바스크립트 알고리즘 문제풀이 입문(코딩테스트 대비)
답이 3이 되어야 하지 않은가요?
제가 이해를 잘 못하는건지 단순 정렬후4번째 학생을 할인 받고 누적을 구하면 29가 나옵니다.그러면 3번째 학생까지 구매를 해줄수 있는것이니 답이 3이 나와야지 않을까 싶습니다.
-
미해결[하루 10분|Web Project] HTML/JS/CSS로 나만의 심리테스트 사이트 만들기
클론코딩 시
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 어떤 웹페이지를 클론 코딩할때 검사에 들어가서 요소만 보고도 똑같이 만드는게 가능한가요? 비슷하게는 되는데 한계가 있는 거 같아서요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
docker 이미지는 한개만 만들 수 있나요?
docker 빌드하고 이미지를 4시간 전에 만들었는데방금 또 빌드하고 이미지 했더니 4시간 전에 만든거라고 나왔어요.그리고 IMAGES ID도 똑같네요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
자바스프링 병행
지금 nodejs 듣고 있는데제가 중소기업해서 취업해서 나중에 대기업 가고 싶은데일단 중소기업 취업 먼저 생각해보면 node만 공부하는게 낫나요
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
비동기 방식에서 콜백함수 사용 이유
강의 10분 경 비동기 방식에서 코드 실행 결과를 확인하기 위해 콜백함수를 사용해야 한다고 하셨는데, 아래와 같이 예시를 들어주신 콜백함수를 사용하는 코드와 그렇지 않는 코드의 다른 점이 무엇인지 궁금합니다. 두 코드의 결과는 똑같이 -1 -> 12 -> 7 순서로 콘솔창에 출력됩니다.첫 번째는 예시를 들어주신 코드이고, 두 번째는 왜 콜백함수를 써서 결과를 봐야하지??라는 궁금증으로 수정해본 코드입니다. function taskA(a, b, cb) { setTimeout(() => { const res = a + b; cb(res); }, 3000); } function taskB(a, b, cb) { setTimeout(() => { const res = a * b; cb(res); }, 2000); } function taskC(a, b, cb) { setTimeout(() => { const res = a - b; cb(res); }, 1000); } taskA(3, 4, (res) => { console.log(res); }); taskB(3, 4, (res) => { console.log(res); }); taskC(3, 4, (res) => { console.log(res); }); taskA1(); // 이후 실행 console.log("코드 끝"); // taskA1 를 기다리지 않고 // 먼저 실행function taskA(a, b) { setTimeout(() => { const res = a + b; console.log(res); }, 3000); } function taskB(a, b) { setTimeout(() => { const res = a * b; console.log(res); }, 2000); } function taskC(a, b) { setTimeout(() => { const res = a - b; console.log(res); }, 1000); } taskA(3, 4); taskB(3, 4); taskC(3, 4);
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
백엔드는 명령어 많이 알아야 하나요?
vscode에서 명령어 입력하고 docker 실행 명령어도 입력했는데 powershell이나 다른 프로그램에서도 명령어 많이 알아야하나요
-
미해결처음 만난 리액트(React)
(실습) JSX 코드 작성해보기
이 화면이 나오게 하고 싶습니다. npm start 엔터 이후로 어떻게 해야 하는지 모르겠습니다
-
미해결처음 만난 리액트(React)
섹션 2 create-react-app
npx create-react-app my-app이라고 작성했는데 오류가 나타납니다. node.js와 npm버전은 모두 14.0과 6.14 이상입니다PS C:\Users\이민준> npx create-react-app my-app npm ERR! code ENOENT npm ERR! syscall lstat npm ERR! path C:\Users\이민준\AppData\Roaming\npm npm ERR! errno -4058 npm ERR! enoent ENOENT: no such file or directory, lstat 'C:\Users\이민준\AppData\Roaming\npm' npm ERR! enoent This is related to npm not being able to find a file. npm ERR! enoent npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\이민준\AppData\Local\npm-cache\_logs\2023-08-02T12_46_01_759Z-debug-0.log PS C:\Users\이민준>
-
미해결자바스크립트 알고리즘 문제풀이 입문(코딩테스트 대비)
이 코드도 맞을까요?
이렇게 풀어도 맞을까요?function solution(necc, course) { let answer = "YES"; let lst = []; for (c of course) { for (n of necc) { if (c === n) { lst.push(c); break; } } } let compare = lst.join(""); if (necc !== compare) answer = "NO"; return answer; } console.log(solution("ABC", "ACBC"));
-
미해결처음 만난 리액트(React)
섹션 2 리액트 시작하기 (실습) 직접 리액트 연동하기
click 버튼이 나타나지 않습니다.코드는 정상적으로 오타가 없는지 여러 번 확인했습니다.function MyButton(props){ const [isClicked, setIsClicked] = React.useState(false); return React.createElement( 'button', {onClick: () => setIsClicked(true)}, isClicked ? 'Clicked!' : 'Click here!' ) } <html> <head> <title>민준의 블로그</title> <link rel="stylesheet" href="sylesj.css"> </head> <body> <h1>민준의 블로그에 오신 여러분들 환영합니다</h1> <div id="root"></div> <!--리액트 가져오기--> <script src="https://unpkg.com/react@17/umd/react.decelopment.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react.decelopment.js" crossorigin></script> <!--리액트 컴포넌트 가져오기--> <script src="MyButtonj.js"></script> </body> </html>
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
onEdit setData 없어도 되는건가요?
안녕하세요 강사님!강의를 따라가는 도중 제가 onEdit 함수에 setData를 하지 않았다는 사실을 알게 되었습니다.하지만 setData를 해주지 않고 data에 map 함수로 변경 해주는 동작만 해도 정상적으로 수행되는데 왜 그런지 알 수 있을까요?useCallback 해주기 전에도 정상 동작한 이유도 궁금하고 강사님께서 알려주신 대로 data에는 컴포넌트가 마운트 됐을때 data 상태인 빈 배열이 들어가 있어야 하는 것이 아닌가요?아래는 제가 짠 onEdit 함수 입니다const editDiary = useCallback((targetId, editedContent) => { data.map((e) => e.id === targetId ? {...e, content: editedContent} : e ) }, [])
-
미해결처음 만난 리액트(React)
npm start해도 안됩니다.
index.jsLibrary.jsxBook.jsx
-
미해결한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
일기 추가 시 DiaryEditor 리렌더링 질문
안녕하세요 강사님강의를 듣고 저는 React.memo로 DiaryEditor를 고차 컴포넌트를 만들어 동일한 prop을 받았을 때 리렌더링 되지 않도록 해주고 onCreate 함수를 useCallback을 통해 메모리제이션 하여 같은 함수를 DiaryEditor에 prop으로 전달한다고 이해했습니다그래서 제가 생각했을 땐 일기를 새로 저장하거나, 삭제, 수정하는 작업 중에는 DiaryEditor가 아예 리렌더링이 되지 않아야 된다고 생각했는데 강의 14분 17초에서도 그렇고 제가 직접 짠 코드에서도 일기를 새로 저장할 때 리렌더링이 되는데 제가 이해한게 잘못 된건가 싶어 질문 드립니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
이메일 템플릿 이메일 전송
구글 앱 비밀번호 설정 떄문에 막힙니다 2단계 인증했고 앱 비밀번호 생성해서 그걸 구글 비밀번호로 바꾸고 선생님 코드를 잘 따라해서 적었는데 Error: Invalid login: 535-5.7.8 Username and Password not accepted.라고 에러 납니다혹시 방법 없을까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
apollo 서버 코드 질문합니다
질문입니다type Mutation {createBoard(createBoardInput: CreateBoardInput!):String } 에서맨 마지막에 왜 createBoard는 String이 붙고type Query{fetchBoards: [MyResult]}에서 fetchBoards는 맨 마지막에 String이 안 붙나요?fetchBoards: [MyResult]:String 이렇게 될수도 있지 않나요 ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ const typeDefs = `#graphqltype Query{fetchBoards: [MyResult]}type Mutation {createBoard(createBoardInput: CreateBoardInput!):String } `;const resolvers = {Query: {fetchBoards: (parent, args, context, info) => {const result = [ { number: 1, writer: "철수", title: "제목입니다", contents: "내용이에요", },{number: 2,writer: "영희", title: "제목입니다",contents: "영희이에요",},{ number: 3, writer: "훈이", title: "제목입니다",contents: "훈이이에요", },];return result;} },Mutation: { createBoard: (_, args) => { console.log(args); onsole.log("========================="); console.log(args.createBoardInput.writer);console.log(args.createBoardInput.title);console.log(args.createBoardInput.contents);return "게시물 등록에 성공하였습니다"; }, }, };
-
미해결자바스크립트 제대로 배워볼래?
Async Await 오류가 나서 문의드립니다.
안녕하세요.스크립트는 아래와 같이 작성하였으며,콘솔에는 3-12.AsyncAwait.html:20 Uncaught (in promise) TypeError: data is not iterableat calculateSum 이라는 오류가 나왔습니다.<!DOCTYPE html> <html> <head> <title>Document</title> </head> <body> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> var url ="https://74f8c451-bcda-46f0-820b-f63caee12b28.mock.pstmn.io/productList"; async function getData2(){ return (await axios.get(url)).data; } async function calculateSum(){ var data = await getData2(); var total = 0; for(var item of data){ total += item.price; } console.log(total); } calculateSum(); </script> </body> </html>어디가 잘못된걸까요...강의랑 똑같이 작성을 한거같은데..
-
해결됨[리뉴얼] 코딩자율학습 제로초의 자바스크립트 입문
비동기 코드 질문
호출스택에 먼저 (anonymous)가 있고 a를 호출하니 a함수가 그 위에 쌓이고그 다음 setTimeout은 비동기코드이니까 백그라운드에 있다가 콜백함수가 태스크큐를 거쳐 이벤트루프를 타고 호출스택으로 가기전에 호출스택이 비어져있어야 콜백함수가 호출스택으로 가는데 a안에 setTimeout이 있으니 동기코드가 아직 안끝났는데 콜백함수가 어떻게 호출스택으로 갈 수 있나요?동기코드는 그 안의 비동기코드가 아직 안끝났어도 자기 할일이 다 끝나면 호출스택에서 벗어나는건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
학습조언 해주세요
인강 40분짜리 3개 듣고 복습해서 총 5시간을 소비했는데 잘 하고 있는 건지 궁금해요