묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[2024] 비전공자도 가능한 React Native 앱 개발 마스터클래스
섹션5. 날씨 앱 expo 버전
확실히 expo 버전으로 프로젝트를 생성하면, 영상처럼 진행이 되지 않습니다 ㅠㅠ 프로젝트 설치 및 실행 과정은 다음과 같습니다.기종: Androidnpx create-expo-app <앱 이름> --template세 번째(blank) :typescript 선택npx expo start를 실행expo 앱에서 QR 코드로 보기강의와 달리, expo로 빌드하셨다면npx expo install expo-location를 설치해주신 다음에 useEffect 함수 부분을 다음과 같이 변경해주면 됩니다.(Expo는 자체 위치 서비스 API를 제공하므로, @react-native-community/geolocation 대신 expo-location을 사용해야 합니다.)useEffect(() => { const getLocation = async () => { try { // 위치 권한 요청 let { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { setError('위치 권한이 거부되었습니다'); setLoading(false); return; } // 현재 위치 가져오기 let location = await Location.getCurrentPositionAsync({}); const { latitude, longitude } = location.coords; fetchWeather(latitude, longitude); } catch (err) { setError(err.message); setLoading(false); } }; getLocation(); }, []);다음으로 OpenWeatherMap API는 기본적으로 켈빈 온도를 반환합니다. 따라서 섭씨로 변환하는 코드를 추가해야 합니다.각자 가져오신 API 값 뒤에 &units=metirc 를 추가해주시면 됩니다. 전체 코드는 다음과 같습니다.import { StatusBar } from 'expo-status-bar'; import { useEffect, useState } from 'react'; import { ActivityIndicator, Text, View } from 'react-native'; import styled from 'styled-components/native'; import * as Location from 'expo-location'; const Container = styled.View` align-items: center; margin-top: 40px; background-color: #222222; flex: 1; `; const MainTemp = styled.Text` font-size: 80px; color: white; `; const Addition = styled.View` background-color: #aeaeae; width: 150px; height: 150px; margin: 15px; align-items: center; justify-content: center; border-radius: 15px; `; const App = () => { const [weatherData, setWeatherData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const getLocation = async () => { try { // 위치 권한 요청 let { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { setError('위치 권한이 거부되었습니다'); setLoading(false); return; } let location = await Location.getCurrentPositionAsync({}); const { latitude, longitude } = location.coords; fetchWeather(latitude, longitude); } catch (err) { setError(err.message); setLoading(false); } }; getLocation(); }, []); const fetchWeather = async (latitude, longitude) => { try { const response = await fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=<<개인 API값>>&units=metric` ); // <<>>는 지우셔야 합니다. if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); setWeatherData(data); } catch (error) { setError(error.message); } finally { setLoading(false); } }; if (loading) { return ( <Container> <ActivityIndicator size="large" color="#ffffff" /> </Container> ); } if (error) { return ( <Container> <Text style={{ color: 'white', fontSize: 20 }} >{`Error: ${error}`}</Text> </Container> ); } if (!weatherData) { return ( <Container> <Text style={{ color: 'white', fontSize: 20 }}>No data available</Text> </Container> ); } const { main: { temp, feels_like, humidity, temp_max, temp_min }, wind: { speed }, weather, } = weatherData; return ( <Container> <StatusBar /> <Text style={{ fontSize: 20, marginTop: 40, color: 'white' }}> {weatherData.name} </Text> <MainTemp>{Math.round(temp)}℃</MainTemp> <Text style={{ fontSize: 24, color: 'white' }}>{weather[0].main}</Text> <View style={{ flexDirection: 'row' }}> <Addition> <Text style={{ color: 'white', fontSize: 20, marginTop: -40, marginBottom: 30, }} > 체감 온도 </Text> <Text style={{ color: 'white', fontSize: 18 }}> {Math.round(feels_like)}℃ </Text> </Addition> <Addition> <Text style={{ color: 'white', fontSize: 20, marginTop: -40, marginBottom: 30, }} > 습도 </Text> <Text style={{ color: 'white', fontSize: 18 }}>{humidity}%</Text> </Addition> </View> <View style={{ flexDirection: 'row' }}> <Addition> <Text style={{ color: 'white', fontSize: 20, marginTop: -40, marginBottom: 30, }} > 최고/최저 온도 </Text> <Text style={{ color: 'white', fontSize: 18 }}> {Math.round(temp_max)}℃/{Math.round(temp_min)}℃ </Text> </Addition> <Addition> <Text style={{ color: 'white', fontSize: 20, marginTop: -40, marginBottom: 30, }} > 풍속 </Text> <Text style={{ color: 'white', fontSize: 18 }}>{speed} m/s</Text> </Addition> </View> <View></View> </Container> ); }; export default App;
-
미해결Next + React Query로 SNS 서비스 만들기
쿠키 로그인 방식에 대한 궁금한 점이 있습니다!
서버 배포후 제공해주는 쿠키의 domain이, .naver.com이고, 만약 로컬호스트로 개발 할 경우 localhost의 도메인을 갖게되어, 둘의 도메인이 달라, 쿠키가 담기지 않는 경우가 발생합니다. 모든 통신 요청을 서버와 직접 진행 할 경우, .naver.com의 쿠키로, 모든것이 공유가 가능하니, 정상적으로 접근이 가능한 것 같은데이제 간혹, 라우트 핸들러나, 미들웨어를 거쳐서, 통신 할 경우 도메인이 localhost가 되어, 401 에러가 발생하게 됩니다!!이를 해결하기 위해, 로컬에서 두개의 서버를 띄워서 테스트하는 방법말고, 보통 실무나, 현업에서는 배포된 쿠키 로그인 방식을 활용할 떄, 어떻게 Next.js에서 환경 구성을 하는지, 로컬호스트가 아닌, 다른 도메인으로 띄울 수 있는 방법이 있는지 궁금합니다.
-
해결됨[2024] 비전공자도 가능한 React Native 앱 개발 마스터클래스
섹션4. 번역기 앱 expo 버전?!
계산기 앱 개발과 마찬가지로, 다음과 같은 프로젝트 설치 및 실행을 진행하였습니다.<기종 : Android>npx create-expo-app <앱 이름> --template세 번째(blank) :typescript 선택npx expo start를 실행expo 앱에서 QR 코드로 보기이전 강의(레이아웃 구성하기)의 중간(7:23)에 나오는 텍스트 창을 클릭하면 UI가 따라 올라가는 것을 방지하기 위한 설정방법이 제 것에서는 나타나지 않았습니다. 검색 창에 windowsoftinputmode 또는 AndroidManifest를 검색해도 나타나지 않았습니다.(아무래도, 제가 설치를 잘못해서 나타났거나 expo로 빌드했기에 나타나지 않는 것 같습니다. VSCode는 기존에 사용했던 터라 해당되지 않는 것 같습니다.) 이를 해결하기 위해,Picker를 감싸고 있는 View를 Wrapper라고 이름을 변경한 후에, postion: absolute 속성을 부여했습니다. +) 텍스트 입력 글자가 꽤 길어지면, 줄바꿈이 되지 않고 글자가 잘려나가는 현상이 있습니다. 이를 해결하는 방법으로는 multiline={true}를 추가해주면 됩니다.코드는 아래와 같습니다.import { StatusBar } from 'expo-status-bar'; import React, { useState } from 'react'; import { Text, TextInput, View, Dimensions } from 'react-native'; import Translator from 'react-native-translator'; import styled from 'styled-components/native'; import { Picker } from '@react-native-picker/picker'; const Container = styled.View` background-color: #181818; flex: 1; padding-top: 20px; `; const Wrapper = styled.View` position: absolute; top: 40px; left: 0; right: 0; padding-horizontal: 10px; `; const InputView = styled.View` background-color: #606060; height: 250px; justify-content: left; align-items: start; padding: 10px; border-radius: 10px; margin-bottom: 80px; `; const ResultView = styled(InputView)` background-color: #0084ff; margin-bottom: 10px; `; const App = () => { const [value, setValue] = useState(''); const [result, setResult] = useState(''); const [fromLang, setFromLang] = useState('en'); const [toLang, setToLang] = useState('ko'); return ( <Container> <StatusBar style="light" /> <Translator from={fromLang} to={toLang} value={value} onTranslated={(t) => setResult(t)} /> <Wrapper> <Picker style={{ marginBottom: 10, backgroundColor: '#606060' }} selectedValue={fromLang} onValueChange={(itemValue) => setFromLang(itemValue)} > <Picker.Item label="한국어" value="ko" /> </Picker> <InputView> <TextInput style={{ fontSize: 24, color: 'white' }} value={value} onChangeText={(t) => setValue(t)} multiline={true} placeholder="번역할 텍스트 입력" /> </InputView> <Picker style={{ marginBottom: 10, backgroundColor: '#0084ff' }} selectedValue={toLang} onValueChange={(itemValue) => setToLang(itemValue)} > <Picker.Item label="영어" value="en" /> </Picker> <ResultView> <Text style={{ fontSize: 24, color: 'white' }}>{result}</Text> </ResultView> </Wrapper> </Container> ); }; export default App; ps) 간혹 글자를 입력하면 즉각 번역되지 않고, 번역되는 아래쪽 Picker를 클릭한 후에야 동작하긴 합니다. 이를 해결하기 위해서는 useEffect를 사용하면 해결되지만 강의에서는 다루지 않아 코드에 반영하지 않았습니다. GPT가 잘 알려주니까 궁금하신 분들은 해보시면 좋을 것 같습니다~ ps) 업데이트 하시면서, 영상이 잘못배치된 것 같습니다~ 번역앱 원리와 이해 부분에 코인에 관한 영상이 재생됩니다. 코인 관련, 날씨앱에서는 이상이 없었습니다~ 좋은 강의 감사합니다~!! 덕분에 많은 도움이 되고 있습니다.
-
해결됨[2024] 비전공자도 가능한 React Native 앱 개발 마스터클래스
계산기 앱 착오를 거친 수정버전
제가 설치를 이상하게 했는지, 잘 되지 않아 저는 프로젝트 생성을 다음과 같은 절차로 진행했습니다.npx create-expo-app <앱 이름> --template세 번째(blank) :typescript 선택npx expo start를 실행expo 앱에서 QR 코드로 보기위와 같은 절차를 거쳐, 복붙으로도 진행해봤을 때의 문제점은 안드로이드 기준으로 기호들이 나타나지 않는다는 점이었습니다. 무지성으로 복붙한 것이 문제였는데요. 문제는 margin 값들의 값이 컸기 때문이었습니다. 혹시 저 같이 잘 안 되시는 분들이 있을까 하여, 좀 헤맸던 기록으로 남깁니다~ ps. 아래 별도로 올려주신 코드 중에 const [result, setResult] = useState('') 에서 useState('0')으로 변경이 필요합니다. 그리고 알려주신 코드를 복붙해서 했을 때 제대로 나오지 않아, GPT의 도움을 받아 수정해보았습니다.import React, { useState } from 'react'; import { Text, View } from 'react-native'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import Entypo from 'react-native-vector-icons/Entypo'; import FontAwesome6 from 'react-native-vector-icons/FontAwesome6'; import styled from 'styled-components/native'; import { evaluate } from 'mathjs'; const NumberButton = styled.TouchableOpacity` background-color: #b6b6b6; width: 65px; height: 65px; border-radius: 35px; margin: 8px; align-items: center; justify-content: center; `; const OperatorButton = styled(NumberButton)` background-color: #0084ff; `; const NumberText = styled.Text` font-size: 30px; color: white; `; const ResultText = styled.Text` font-size: 65px; padding: 20px; margin-top: 50px; color: white; text-align: right; `; const App = () => { const [input, setInput] = useState(''); const [result, setResult] = useState('0'); const handlePress = (value) => { const newInput = input + value; setInput(newInput); setResult(newInput); // 입력값을 바로 결과창에 표시 }; const handleOperation = (op) => { if (input && !['+', '-', '*', '/'].includes(input.slice(-1))) { const newInput = input + op; setInput(newInput); setResult(newInput); // 연산자도 바로 결과창에 표시 } }; const calculate = () => { try { const calculatedResult = evaluate(input).toString(); setResult(calculatedResult); setInput(calculatedResult); // 계산 결과를 새로운 입력값으로 설정 } catch (e) { setResult('Error'); setInput(''); } }; const clear = () => { setInput(''); setResult('0'); }; return ( <View style={{ flex: 1, backgroundColor: '#181818' }}> <ResultText>{result}</ResultText> <View style={{ flex: 1, justifyContent: 'flex-end', paddingBottom: 20 }}> <View style={{ flexDirection: 'row', justifyContent: 'center' }}> <View style={{ paddingRight: 20 }}> <View style={{ flexDirection: 'row', flexWrap: 'wrap', width: 250 }} > {[...Array(10).keys()].reverse().map((num) => ( <NumberButton key={num} onPress={() => handlePress(num.toString())} > <NumberText>{num}</NumberText> </NumberButton> ))} <NumberButton onPress={clear}> <MaterialIcons name="restart-alt" size={30} color="white" /> </NumberButton> <NumberButton onPress={() => handlePress('.')}> <Entypo name="dot-single" size={30} color="white" /> </NumberButton> </View> </View> <View style={{ marginTop: -81 }}> <OperatorButton onPress={calculate}> <MaterialCommunityIcons name="equal" size={30} color="white" /> </OperatorButton> <OperatorButton onPress={() => handleOperation('+')}> <Entypo name="plus" size={30} color="white" /> </OperatorButton> <OperatorButton onPress={() => handleOperation('-')}> <Entypo name="minus" size={30} color="white" /> </OperatorButton> <OperatorButton onPress={() => handleOperation('*')}> <Entypo name="cross" size={30} color="white" /> </OperatorButton> <OperatorButton onPress={() => handleOperation('/')}> <FontAwesome6 name="divide" size={30} color="white" /> </OperatorButton> </View> </View> </View> </View> ); }; export default App;
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
revalidateTag 사용 시 ReviewDetail 리렌더링
revalidateTag를 사용하더라도 결국 Review page가 모두 리렌더링 되는 것으로 보입니다. 이는 자연스러운 동작일까요? 그렇다면,ReviewDetail에서 사용하고 있는 API (책 상세 정보 가져오기)는 굳이 호출될 필요가 없는데 추가 호출이 되는 것 아닐까요? 이런 상황에서 revalidateTag를 사용하는 게 좋은 방법이 맞는지 궁금합니다!
-
미해결[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
5.4 버튼 새로정렬됨
위에 그림이 제가한 코드를 돌렸을떄 나오는 그림인데 강의 실습에서는 버튼이 새로로 정렬되지않고 위에 붙어있는 형식이런 형식으로 나왔는데 코드에 차이점이 아예 없어 뭐가 문제인지 궁금합니다
-
해결됨[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
섹션 12강의 Context에 대한 질문
섹션 12. Context의 > 11.3 분리하기에 대한 질문입니다.강의에서 처럼 TodoDispatchContext로 분리한 후 함수인 { onCreate, onUpdate, onDelete } 에 대해 useMemo처리를 함으로 재생성이 일어나지 않도록 하는 것에 대해 잘 이해되었습니다. 그런데 궁금한 것은, 그렇다면 위 객체에서 처리한 useCallback은 필요없는 것은 아닌지요? 문의드립니다.
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
그랩선생님, [컴포넌트 사용하기] 강의에서 질문 있습니다.
안녕하세요? 그랩선생님, 수고가 많으십니다.다름이 아니라 강의 [컴포넌트 사용하기] 강의를 진행하던 중 2가지 에러가 발생하여 질문을 하게 되었습니다.첫번째 에러는 아래 사진으로 첨부합니다. 그리고 두번째 에러는 실제 소스 파일 폴더에 가서 App.js 파일을 선택 후 엔터(실행)하니 아래 사진과 같은 에러가 발생하였습니다. --> 이 두가지 에러를 어떻게 하면 해결 할 수 있는지 상세한 설명과 자세한 답변 부탁드립니다.감사합니다.
-
해결됨[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
getTime( ) 메서드 사용 질문
getTime() 메서드 사용 질문드립니다home pivoteDate -> new Date() startTime, EndTime => getTime()new onSubmit -> onCreate ( input.createdDate.getTime(), input.emotion,input.content ) const [input, setInput] = useState({ createdDate: new Date(), emotionId: 3, content: "", });Edit const onSubmit = (input) => { onUpdate( params.id, input.createdDate.getTime(), input.emotionId, input.content ); }; getTime() 메서드를 썼다 안 썼다 하는 것 같은데 언제 쓰고 안 쓰는지 이유가 궁금합니다.
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 (장고 4.2 기준)
템플릿 관련 질문입니다.
안녕하세요 강사님.템플릿이 명확하게 정의가 되지 않는데, 혹시 템플릿 한번만 정의해주시면 안될까요, 저는 지금 두리뭉실하게만 머리속에 있습니다..질문 받아주셔서 감사합니다.
-
해결됨따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
로그아웃 401 에러(Unauthorized)
로그인 후 개발자 도구 쿠키에 x_auth 정상적으로 확인DB에도 토큰 정상적으로 값 입력된 것 확인로그아웃 버튼을 클릭 시 401 에러 발생, 토큰 삭제 xserver/index.jsapp.post('/api/users/login', async (req, res) => { try { const user = await User.findOne({ email: req.body.email }); if (!user) { return res.json({ loginSuccess: false, message: "제공된 이메일에 해당하는 유저가 없습니다." }); } // 비밀번호가 일치하는지 확인한다. const isMatch = await user.comparePassword(req.body.password); if (!isMatch) { return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다." }); } // 비밀번호가 일치하다면 토큰을 생성한다. const tokenUser = await user.generateToken(); res.cookie("x_auth", tokenUser.token) .status(200) .json({ loginSuccess: true, userId: user._id }); } catch (err) { return res.status(400).send(err); } }); app.post('/api/users/logout', auth, async(req, res) => { try { await User.findOneAndUpdate({ _id: req.user._id }, { token: "" }); res.clearCookie("x_auth"); return res.status(200).send({ logoutSuccess: true }); } catch (err) { return res.json({ logoutSuccess: false, message: err.message }); } });User.jsuserSchema.methods.comparePassword = function(plainPassword, cb) { const user = this; return bcrypt.compare(plainPassword, user.password); }; userSchema.methods.generateToken = function() { var user = this; // jsonwebtoken을 이용해서 토큰을 생성한다. var token = jwt.sign(user._id.toJSON(), 'secretToken'); user.token = token; return user.save(); }; userSchema.statics.findByToken = async function(token) { const user = this; try { // 토큰을 decode합니다. const decoded = jwt.verify(token, 'secretToken'); // 디코딩된 정보를 이용해 유저를 찾습니다. const userData = await user.findOne({ "_id": decoded, "token": token }); return userData; } catch (err) { throw new Error("유효하지 않은 토큰입니다."); } };auth.jsconst { User } = require("../models/User"); function auth(req, res, next) { // 인증 처리한다. // 클라이언트 쿠키에서 토큰을 가져온다. const token = req.cookies.x_auth; if (!token) { return res.status(401).json({ isAuth: false, message: "토큰이 제공되지 않았습니다." }); } // 토큰을 복호화한 후 유저를 찾는다. User.findByToken(token) .then((user) => { if (!user) { return res.status(401).json({ isAuth: false, message: "유효하지 않은 토큰입니다." }); } // 토큰과 유저정보를 다음 단계로 전달한다. req.token = token; req.user = user; next(); }) .catch((err) => { return res.status(401).json({ isAuth: false, message: err.message }); }) }; module.exports = { auth };서버의 경로를 절대 경로로 기입하지 않으면 404 에러 발생5000 포트로 데이터 전송, cors로 3000 포트로 이동하도록 설정const corsOptions = { origin: 'http://localhost:3000', // 클라이언트 주소 credentials: true // 쿠키를 포함하도록 }; app.use(cors(corsOptions));
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
error.tsx를 app 폴더 바로 아래에 둬도...
error.tsx를 app 폴더 바로 아래에 둬도 footer에서 발생하는 오류는 잡히지 않습니다. app layout에서 발생하는 에러는 error.tsx로 잡히지 않는걸까요?
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
/book/[id]/page.tsx에서 book type은 왜 지정하지 않나요?
book type은 왜 지정하지 않는지 궁금합니다.그리고 왜 book type은 지정하지않아도 typescript에서 체킹(?)을 하지 않는지도 궁금합니다. 항상 강의잘보고있습니다. 감사합니다. 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 질문 하시기 전에 꼭 확인해주세요- 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다)- 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다)- 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 )질문 하실때 꼭 확인하세요- 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X)- 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지)- 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우)- 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요- 강의의 몇 분 몇 초 관련 질문인지 알려주세요!- 서로 예의를 지키며 존중하는 문화를 만들어가요. - 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
암호가 해싱되지 않고 입력값 그대로 db에 저장되는 문제
뭔가 빠트렸나 했더니, isModified() 때문에 비밀번호 변경이 아닌 신규 데이터 저장시에는 next() 를 호출해버리네요에러도 없어서 파악이 좀 오래걸렸네요 if(user.isModified("password") 부분을 !user.isModified("password") 로 수정하고 해결되었네요 ※ 같은 문제 겪으시는분 계시면 참고하세요!
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
PageRouter 프로젝트 생성
PS C:\Users\Master\Desktop\onbite-next> npm run devnpm ERR! Missing script: "dev"npm ERR! npm ERR! To see a list of scripts, run:npm ERR! npm runnpm ERR! A complete log of this run can be found in: C:\Users\Master\AppData\Local\npm-cache\_logs\2024-11-18T14_22_23_262Z-debug-0.log이런 오류가 발생했습니다..
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
9.4) 최적화 후 배포시 빌드시 /book/bookId 의 프리렌더링 에러가 발생하는거 같아요
안녕하세요, 9.4 강의 최적화 후 배포 빌드때 문제가 발생했습니다.- 에러메시지book/[id]/page.tsx 소스코드server는 이미 배포가 되어있고.. vercel 서버 URL도 환경설정 잘 적용된거 확인했고.로컬에서 npm run build까지 잘되었는데혹시 any타입 빌드를 해결하려고 (.)book/[id]/page.tsx에서 이런식으로 프롭스를 넘겨서 발생한 문제일까요..?
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
스트리밍 SEO 질문드립니다.
스트리밍은 Fetch 등 서버 컴포넌트에서 비동기 실행되는 것들이 완료되기 전에 로딩 화면 같은 것을 보여주는 것이라 이해했습니다. 그렇다면, 스트리밍을 사용하기 않는다면 API를 호출하고 이를 기다린 후, 이에 대한 데이터가 HTML에 포함되어 SEO에 도움을 줄 것 같은데요.스트리밍을 사용한다면 HTML에 로딩 데이터가 보여집니다. 이를 해결할 수 있는 방법이 있나요? 아니면, 이는 SEO를 고려하지 않는 곳에서 사용해야 하는 기법인지 궁금합니다.
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
실습용 백엔드 서버 세팅
PS C:\Users\Master\Desktop\onebite-books-server-main> npx prisma db pushEnvironment variables loaded from .envPrisma schema loaded from prisma\schema.prismaDatasource "db": PostgreSQL database "postgres", schema "public" at "aws-0-ap-northeast-2.pooler.supabase.com:6543"이렇게 뜨고 성공했다는 문구는 안 뜹니다..
-
미해결Next + React Query로 SNS 서비스 만들기
배포하신 깃헙 auth.ts signOut 질문입니다
https://github.com/ZeroCho/z-com/blob/master/src/auth.ts#L16배포하신 깃헙에서 궁금한점이 생겨서 질문드립니다1. signOut에서 백엔드로 로그아웃 요청만 보내고 authorize()처럼 쿠키값을 set해주지 않았으니 connect.sid는 남아있는게 맞나요? 2. next auth 세션값이 지워지면서 프론트서버와 인증이 끊기는데 프론트서버에서 굳이 백엔드로 요청을 보내는 이유가 뭘까요? 백엔드 서버에 로그 남기기 위해서인가요?
-
해결됨[리액트 2부] 고급 주제와 훅
[4.4장 메모이제이션 훅] 4.4.3 memo 참조 비교
안녕하세요. 강사님! 학습 중에 궁금한 점이 생겼는데, 제가 이해하고 있는 내용이 맞는지 궁금해 질문 드립니다!const obj1 = { a: 1 }; const obj2 = { a: 1 }; const obj3 = obj1; console.log(obj1 === obj2); // falseconsole.log(obj1 === obj3); // true 자바스크립트에서 객체나 배열을 비교할 때 기본적으로 참조를 비교하는 것으로 알고 있습니다. 즉, 1번에서 두 객체가 같은 값을 가지고 있으나 메모리 주소가 달라 false를 출력하고,2번에선 같은 메모리 주소를 참조해 true를 출력하는 것으로 알고 있습니다여기서부터 제가 궁금했던 내용입니다!캐시된 filteredPosts를 사용해 렌더링하는 경우를 가정했을 때 :const filteredPosts = MyReact.useMemo(filterPosts, [posts, tag]);filteredPosts는 useMemo를 통해 filterPosts 함수로 생성된 배열을 클로저 공간에 넣어두고, posts와 tag가 변경되지 않는다면 항상 똑같은 값을 FilteredPosts로 전달 =><FilteredPosts posts={filteredPosts} /> const FilteredPosts = MyReact.memo(({ posts }) => { ... } // FilteredPosts는 캐시된 filteredPosts를 memo 함수로 전달 =>function memo(TargetComponent) { return (nextProps) => { ... const [prevValue, prevProps] = TargetComponenet.memorizedState; if (prevProps === nextProps) { return prevValue; } ... } }그럼 여기 전달된 nextProps(캐시된 filteredProps)는 당연히 같은 메모리를 참조하니 깊은 비교 없이 참조 비교만으로도 비교가 가능하지 않나? 싶었습니다.filteredPosts는 결과적으로 useMemo로 캐시되어있고, 새로 생성된 배열이 아니니 메모리 주소가 동일해 prevValue가 리턴되지 않을까? 했는데 아니더라구요..useMemo를 통해 filteredPosts 배열의 참조는 동일하게 유지되고 있는건 맞는데,{ posts: filteredPosts }이렇게 FilteredPosts 컴포넌트에 전달되는 props 객체가 매번 새로운 객체로 생성돼서 그런건가? 싶은데 이게 맞는지 궁금해서 질문 드립니다..