묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
docker 명령어 에러 떠요
docker rm `docker ps -a- q'라고 입력했는데 unknown shorthand flag: 'a' in -a- 라고 에러 뜹니다
-
미해결처음 만난 리액트(React)
빌드 후 serve가 안됩니다!
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 유사한 질문이 있어서 권한 설정도 확인해봤는데 문제없었습니다.문제가 뭘까요?참고로 저는 패키지 매니저를 npm대신에 yarn을 사용했습니다!yarn global add serve 까지 해둔 상태입니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
jwtstrategy error
안녕하세요 원두멘토님ㅎㅎ 이런 에러가 계속 떠서 docs와 스텍오버플로우에 검색을 해보니 Passport-jwt를 import하는 것이 아닌 passport-local을 import하는 것이라해서 했더니 다행히 실행이 됩니다.수업내용에서는 passport-jwt로만 해도 작동이 잘되는 것 같은데 왜 이런 문제가 생기는 것일까요?그리고 passport-local로 하면 실제 배포에서는 사용 못하는 것이 아닌지 질문을 남깁니다.p.s 재작년에 코캠2기 수료하고 백엔드를 많이 기다렸는데 이렇게 인프런에서 다시 인강으로 뵙게 되서 너무 반갑습니다
-
해결됨타입스크립트 입문 - 기초부터 실전까지
타입을 전역으로 분리
타입을 파일을 따로 분리해서 import 해서 사용해도 되는걸까요?
-
미해결만들면서 배우는 리액트 : 기초
html 코드 오류
안녕하세요 강의 초반부에script 태그 3개를 추가했는데도 catItem내에서 html 코드를 쓰면 오류가 뜨네요이유가 뭘까요 ㅠㅠUncaught SyntaxError: Inline Babel script: Unexpected token (5:6) (at babel.min.js:7:10099) 3 | 4 | const catItem = { > 5 | < li > | ^ 6 | < img src="https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react" /> 7 | </li > 8 | }<body> 아래는 제가 작성한 코드입니다. 주석 부분은 지웠습니다. <body> <div id="app"></div> <!-- <h1>1번째 고양이 가라사대</h1> <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> console.log("야옹"); const catItem = { <li> < img src="https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react" /> </li> } const 여기다가그려 = document.querySelector("#app"); ReactDOM.createRoot(여기다가그려).render(catItem); </script> </body>
-
미해결애플 웹사이트 인터랙션 클론!
페이지가 처음 로딩 되었을 때 애니메이션 처리가 되지 않는 느낌입니다
페이지가 맨 처음 로딩 되었을 때에는 애니메이션이 작동하지 않다가... 페이지 섹션을 일정 부분 진행하고 다시 윗방향 스크롤을 걸었을때 애니메이션이 정상작동을 합니다.그 후부터는 페이지의 시작 위치로 이동해서 시작부터 차근차근 내렸을때 정상작동을 합니다.원인이 잘 생각이 나지 않네요 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초에서도 그렇고 제가 직접 짠 코드에서도 일기를 새로 저장할 때 리렌더링이 되는데 제가 이해한게 잘못 된건가 싶어 질문 드립니다!