9달 전 · 이상래 님의 새로운 답변
react 컴포넌트 작게 쪼갤경우에 파일을 분리하는게 좋나요?
현재 공통으로 사용되지않고, 하나의 page에서만 사용되는 컴포넌트들의 경우 pages폴더의 signUp 폴더에서 (회원가입 페이지의 경우) 계층으로 작성하고있습니다. 단일책임원칙에 따라 하나의 컴포넌트가 하나의 일만 하도록 작게 쪼개고있는데, 폴더의 뎁스도 늘어나고 파일이 너무 많아지는거같아 이렇게 하는 방식이 옳은것인가? 라는 의문이 들어 질문을 작성합니다. 이처럼 컴포넌트를 하나의 일만 하도록 최대한 작게 쪼개는게 옳은 방식인지... 그렇다면 폴더를 계층으로 쪼개는게 맞는건지... 알려주시면 감사하겠습니다 ! 한가지 예시 signUp/content /form / buttonGroup.tsx 요런식입니다
개발자
#react
답변 1
댓글 0
조회 53
일 년 전 · 삭제된 사용자 님의 댓글 업데이트
안녕하세요.. 정말 이것저것 다 해봤는데 안되네요 ㅠ
안녕하세요. 리액트와 리액트 쿼리를 이용해 프로젝트를 진행 중 입니다. 저는 일단. src/hooks/useProduct.jsx 라는 커스텀 훅 을 만들었습니다. 제 계획은 1. 맨 처음 최상단 Home.jsx 에서 useProduct 라는 커스텀 훅 호출 2. 자식컴포넌트에 data를 전달 3. Search.jsx에서 받은 filter값으로 커스텀 훅 안의 query 재실행 4. 바뀐 상태는 Home.jsx 가 다시 받고 자식 컴포넌트에게 전달. 1~3까지는 콘솔찍어가면서 잘 되는데 4.는 되지 않습니다. (쿼리 데이터가 바뀌어도 4이 재 실행이 안됨..) ㅠㅠㅠㅠㅠㅠㅠ 쿼리 옵션도 줘보고 전역 상태도 만들어보고 이것저것 다 해봤는데 ㅠㅠ 혹시 아시는분 계신가요..? 어떠한 답변도 정말 감사히 받겠습니다 간략 코드첨부) Home.jsx const Home = ()=>{ const { data, error, isLoading } = useProduct(); return ( <Search /> <List data={data?.product} /> ); }; export default Home; Search.jsx const Search = () => { ```일부코드생략``` const { updateQuery } = useProduct (); const handleSubmit = () => { const searchData = { productId }; updateQuery(productId); }; } return( ```일부코드생략``` <button onClick={handleSubmit}> 조회 </button> ) } export default Search; useProduct.jsx const useProduct = () => { const [queryData, setQueryData] = useState(); const { data, error, isLoading } = useQuery({ queryKey: ["product", queryData], queryFn: () => fetchProduct(queryData), }); const updateFilters = (queryData) => { setQueryData(() => (queryData); }; return { data, error, isLoading, updateQuery , }; export default useProduct ;
개발자
#react
#react-query
#react-query-v5
답변 3
댓글 6
조회 116
일 년 전 · 털먹는토끼 님의 댓글 업데이트
리액트 쿼리 에러 핸들링 이슈
react query를 활용한 에러 핸들링이 안돼서 질문 올립니다... //mainpage.tsx const { data, refetch, isFetching } = FetchData(url); const handleSearch = () => { refetch(); } <QueryErrorResetBoundary> {({ reset }) => ( <ErrorBoundary onReset={reset} FallbackComponent={FallbackUI}> {resultVisible ? ( <Result searchData={searchData} isFetching={isFetching} /> ) : ( <EmptyResult /> )} </ErrorBoundary> )} </QueryErrorResetBoundary> react-error-boundary 라이브러리를 이용해 ErrorBoundary 컴포넌트를 가져왔습니다. ErrorBoundary 하위 컴포넌트 내에서 에러가 발생하면 FallbackComponent의 FallbackUI 컴포넌트가 렌더링 되도록 했습니다. //FallbackUI 컴포넌트 //에러가 발생히면 이 컴포넌트가 렌더링되어야합니다. const FallbackUI = ({ error, resetErrorBoundary }) => { return ( <div> <span>{error.message}...</span> <button onclick={resetErrorBoundary} /> 돌아가기 버튼 </div> ); }; export default FallbackUI; FallbackUI 에서 QueryErrorResetBoundary 에서 제공하는 resetErrorBoundary를 받아와 에러 발생 후 '돌아가기 버튼'을 클릭하면 쿼리오류를 처리하고 리셋해주도록 구현했습니다. //url을 파라미터값으로 받아와 api호출하는 함수 //위에 있는 mainpage.tsx 에서 사용하는 함수입니다. const FetchData = (url: string) => { const { error, data, refetch, isFetching } = useQuery({ queryKey: ["repoData"], queryFn: async () => { const res = await axios.get(url); console.log(res.data); return res.data; }, refetchOnWindowFocus: false, enabled: false, }); return { error, data, refetch, isFetching }; }; 리액트 쿼리를 이용해 api를 호출하는 함수입니다. //main.tsx const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0, throwOnError: true, }, }, }); ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <BrowserRouter> <GlobalStyles /> <Provider store={store}> <App /> </Provider> </BrowserRouter> </QueryClientProvider> </React.StrictMode> ); 마지막으로 전역으로 에러 관리를 하도록 세팅해놨습니다. 그리고 QueryClient에 throwOnError 속성이 있어야 에러를 ErrorBoundary로 전달할 수 있다해서 추가해줬습니다. 이렇게 세팅해놨는데 에러발생하면 그냥 하얀색 화면만 나오고 fallbackUI가 나오지 않습니다... 원인이 뭘까요...ㅠㅠㅠ 혹시 몰라서 콘솔 에러코드도 올립니다.. 추가로 궁금한 점 1. useQuery 에서 반환하는 객체중 error 객체는 어떤 존재인지 궁금합니다. 2. useQuery 속성과 QueryClient 속성 모두 throwOnError : true 를 가지고 있던데 어떤 차이점인지 궁금합니다. 답변주시면 정말 감사하겠습니다!!!
개발자
#react
#react-query
#error-handler
#error-boundary
답변 1
댓글 1
조회 212
일 년 전 · 방재호 님의 답변 업데이트
[Js] 함수 매개변수 전달에 대하여...
안녕하세요! 이제 막 1년 좀 넘은 주니어 개발자 입니다. 공부 중에 한 가지 궁금한 점이 생겨서 선생님들께 여쭤보려합니다. js 함수를 작성하는 중에 매개변수로 event를 전달하려 했더니 event에 취소선이 생성됩니다... 예시) - html - <div class='.card'> ... <button id="deleteCard" onclick="deleteCard(event)">삭제</button> </div> - js - function deleteCard(event){ let card = $(event.target).closest('.card').remove(); }; 위와 같이 작성했는데 함수 실행은 되긴 하는데 매개변수 event에 취소선이 생기니 잘못된 것 같아서 여쭤봅니다... 취소선이 생기는 이유와 문제가 된다면 어떤 점에서 문제가 생기는지 개선점은 뭐가 있는지 궁금해서 문의 남깁니다.
개발자
#javascript
#매개변수
답변 1
댓글 0
조회 80
일 년 전 · 그린티라떼 님의 새로운 답변
react에서 복잡한 상태를 관리할때.. 한번에 관리? 분리해서 관리? (jotai, recoil, useState)
안녕하세요 react를 이용해서 복잡한 작성 페이지를 만들고있습니다. 아래의 타입은 예시입니다. type payload = { name: string; message: string; type: string; url: { pc: string; mobile: string; }; contents: { images: { url: string; name: string; }[]; buttons: { name: string; url: string; }[]; }; conditions: { ... }, .... }; 위처럼 복잡한 데이터를 서버로 보내줘야해서 데이터를 관리해야하는데 현재는 페이지내에 구조가 복잡하고 컴포넌트도 매우많아 props drilling이 너무 심해질거같아서 recoil, jotai와 같은 상태관리 라이브러리를 이용해서 작업을 진행하고있습니다. 이러한 상황에서 관리하는 데이터를 한객체에 모아서 관리하는게 좋은지 아니면 const nameAtom = atom(''); const messageAtom = atom(''); const typeAtom = atom(''); const urlAtom = atom({ pc: '', mobile: ''}); .... 처럼 일일히 쪼개서 관리하는게 맞는 방향인지 모르겠어서 질문드립니다 ! (사수가없어서 물어볼곳이없어요...) 현재는 아래와같이 쪼개서 작업한뒤 submit시에 합쳐주는 방식으로 구현해놓았는데 atom 갯수가 20개 정도 되버리니까 너무 복잡해보여서 이게맞나... 싶어서 질문드리게되었습니다.
개발자
#react
#frontend
답변 2
댓글 0
보충이 필요해요 1
조회 246
일 년 전 · 김태우 님의 답변 업데이트
Next.js 에러 핸들링
Next.js 에러 핸들링을 위해 루트에 error.tsx파일을 생성했습니다. 특정페이지에서 서버 컴포넌트를 만든 후 `throw new Error` 를 통해 에러 발생시 위에서 생성했던 에러페이지(root/error.tsx)로 대체됩니다. 그러나 클라이언트 컴포넌트("use client")에서 테스트 하기위해, button태그의 onClick을 통해 `throw new Error`를 실행시켰습니다. 이때 콘솔에만 출력되고 따로 에러페이지로 대체 되진않았습니다. 클라이언트 컴포넌트에선 해당 동작을 통해서는 에러페이지로 대체되는 핸들링이 되지않는걸까요?? Next: v14
개발자
#next.js
답변 1
댓글 0
조회 684
일 년 전 · 백승훈 님의 답변 업데이트
Url to state or url and state
흰색 배경의 button group이 있고 각각의 button은 클릭할 시 url을 변경시킵니다. 또한 현재 선택된 button은 색상이 칠해집니다. 이러한 상황에서 useState를 통해서 button의 state를 관리하고 있었고, button을 클릭했을때 1. setState를 통해 button의 값을 변경함 2. router을 통해 url 경로를 변경함 이렇게 두가지 액션을 취했었습니다. 그 때 생겼던 궁금중은 어차피 url과 button은 mapping 할 수 있으니까, 버튼을 클릭하면 url만 변경시키고, url을 읽어서 button에 다시 값을 주면 기능적으로 똑같지 않나? 그러면 하나의 액션으로 기존의 두가지 액션을 대체할 수 있지 않나? 라고 생각했습니다. 그래서 조금 찾아보니까 url을 바꾸고 그 값을 통해 button의 상태 관리를 다시 하는건 즉각적인 반응이 아니고, 영상에서 봤던 것 처럼 url과 button state를 각각 바꾸는게 더 인터랙티브하다 라는 글을 읽었습니다. (신빙성이 있는 글은 아닙니다.) 실제로 이러한 이유 때문에 url만 바꾸고, 그 값을 받아와 button 값을 할당하는게 아니라, 두가지를 모두 동시에 바꾸는게 더 좋은 방법이 맞는건지 궁금합니다.
개발자
#nextjs
답변 1
댓글 0
조회 46
일 년 전 · 민경배 님의 답변 업데이트
리액트 내비게이션 초기 데이터값 설정
import React, { useEffect, useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { TbArrowBigUpFilled } from "react-icons/tb"; import useScroll from "../utils/useScroll"; import useProductsQuery from "../useProductsQuery"; const Nav = () => { const { isNavFixed } = useScroll(); const [activeButton, setActiveButton] = useState(0); const handleButtonClick = (index) => { setActiveButton(index); }; const categoryNames = { materials: "소재별 품목", purposes: "용도별 품목", }; const [isScrollMenuVisible, setScrollMenuVisible] = useState(false); const handleMouseEnter = () => { setScrollMenuVisible(true); }; const handleMouseLeave = () => { // console.log("마우스 이탈"); setScrollMenuVisible(false); }; const location = useLocation(); const isloginorsignup = location.pathname === "/login" || location.pathname === "/signup"; const { isLoading, isError, errorMessage, materials, purposes } = useProductsQuery(); const [hoveredOnedepth, setHoveredOnedepth] = useState(null); const [onedepthChildren, setOnedepthChildren] = useState([]); const [hoveredDivision, setHoveredDivision] = useState(null); const [twodepthDivision, setTwodepthDivision] = useState([]); const handleOnedepthMouseEnter = (item) => { setHoveredOnedepth(item); // console.log(setHoveredOnedepth) setOnedepthChildren(item.children); }; const handletwodepthMouseEnter = (child) => { setHoveredDivision(child); // console.log(setTwodepthDivision); setTwodepthDivision(child.divisions); // console.log(child.divisions); }; if (isLoading) return <>로딩 중...</>; if (isError) return <>{errorMessage}</>; return ( <> {!isloginorsignup ? ( <> {/* 대메뉴 */} <nav id="nav" className={`nav ${isNavFixed ? "fixed" : ""}`}> <div className="nav_wrap"> <div className="twobutton_wrap"> {["materials", "purposes"].map((category, index) => ( <Link type="button" className={`twobutton ${ activeButton === index ? "active" : "" }`} key={index} onClick={() => handleButtonClick(index)} to={`/${category}`} > {categoryNames[category]} </Link> ))} </div> {/* 내비게이션 1depth */} <div className="scrollnav"> <ul className="materialnav" style={{ display: activeButton === 0 ? "flex" : "none", }} > {materials.map((material) => ( <li key={material.id} className="materialnav_li" onMouseEnter={() => { handleOnedepthMouseEnter(material); handleMouseEnter(); }} onMouseLeave={handleMouseLeave} > <Link to="/">{material.name}</Link> </li> ))} </ul> <ul className="usagenav" style={{ display: activeButton === 1 ? "flex" : "none", }} > {purposes.map((purpose) => ( <li key={purpose.id} className="usagenav_li" onMouseEnter={() => { handleOnedepthMouseEnter(purpose); handleMouseEnter(); }} onMouseLeave={handleMouseLeave} > <Link to="/">{purpose.name}</Link> </li> ))} </ul> </div> <div className="call_wrap"> <div className="call"> <div className="call_title">견적 상담 · 문의 대표전화</div> <div className="top_button"> <TbArrowBigUpFilled className="top_icon" /> <p>TOP</p> </div> </div> </div> </div> </nav> {/* 2&3depth 호버 메뉴 */} <div className={`scrollmenu ${isScrollMenuVisible ? "show" : ""} ${ isNavFixed ? "fixed" : "" }`} style={{ visibility: isScrollMenuVisible ? "visible" : "hidden" }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > {activeButton === 0 && ( <div className="scrollmenu_top"> <ul className="depth2_wrap"> {onedepthChildren.map((child) => ( <li key={child.id} className="depth2_menu" onMouseEnter={() => { handletwodepthMouseEnter(child); }} > <div className="depth2_menu_img_wrap"> <div className="depth2_menu_img"> <img src={child.images[0]._links.href} alt={child.name} /> </div> </div> <p>{child.name}</p> </li> ))} </ul> </div> )} <div className="scrollmenu_middle"> <div className="depth3_wrap"> {twodepthDivision.map((division) => ( <Link key={division.id} to="/" className="depth3_menu"> <span className="depth3_menu_img"> {/* images 속성이 없거나 비어 있어도 오류를 방지 */} {division.images && division.images.length > 0 && ( <img src={division.images[0]._links.href} alt={division.division_name} /> )} </span> <p>{division.division_name}</p> </Link> ))} </div> </div> <div className="scrollmenu_bottom"></div> </div> </> ) : null} </> ); }; export default Nav; 현재는 depth2_menu 호버해야만 depth3_menu가 보이는데 초기에 scrollmenu가 보일때부터 depth2_menu가 (onedepthChildren.map의 첫번째 순서의 데이터가) 호버되어있어 그에 매칭되는 division의 내용들이 depth3_menu에 보이게 할수있는 방법 있을까요 ?
개발자
#react
답변 1
댓글 0
조회 116
2년 전 · LHS 님의 질문
Next.js의 dynamic import를 이용한 Ref 전달
현재 next.js 13버전과 toast ui calendar를 사용해서 프로젝트를 진행하고있습니다. toast ui calendar는 서버사이드에서는 렌더링이 되지 않기 때문에 dynamic 함수를 사용해서 import를 했습니다. dynamic 함수를 사용해서 import한 component에 ref를 넘기는 방법이 궁금합니다! https://careerly.co.kr/qnas/2078 위의 질문글을 보고 현재 아래 코드와 같이 작성을했는데 이렇게 작성을 하게된다면 결국 TuiCalendar라는 컴포넌트에는 ref={ref}, forwardRef를 사용해서 넘기는것이 아닌 forwardedRef라는 props로 ref를 넘기는형태가 되는건데 dynamic 함수를 사용해서 받아온 컴포넌트에 ref를 넘기는것은 이방법밖에 없을까요? 또한 "use client" import { useRef } from "react"; import Child from "./child"; export default function Parent (props : any) { const testRef = useRef(3) return ( <> <button onClick={() => console.log(testRef.current)}>버튼</button> <Child testRef={testRef} /> </> ) } 이것과 같이 ref를 그냥 props로 넘기는것과 ref + forwardRef 로넘기는 것의 차이점을 알고싶습니다!
개발자
#next.js
#react
답변 0
댓글 0
추천해요 2
조회 509
2년 전 · 김주호 님의 새로운 답변
리액트 책을 보면서 공부중인데 아래의 코드가 제대로 작동을 않합니다
import { useState } from "react"; type CounterProps = { initialValue: number; }; const Counter = (props: CounterProps) => { const { initialValue } = props; // 계정을 유지하는 하나의 상태를 useState()로 선언한다. 인수에는 초깃값을 지정한다. // count는 현재 상태, setCount는 상태를 업데이트하는 함수다。 const [count, setCount] = useState(initialValue); return ( <div> <p>Count: {count}</p> {/* setCount를 호출해서 상태를 업데이트한다 */} <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button> </div> ); }; export default Counter; 책에 있는거 그대로 코딩 했는데 Property 'initialValue' is missing in type '{}' but required in type 'CounterProps' 이런 에러가 발생해요 왜 그런걸까요
개발자
#react
답변 1
댓글 0
조회 78
2년 전 · 익명 님의 질문
styled-components 모듈 관리는 어떻게 하는게 효율적인가요?
리엑트에서 프로젝트 하실 때, styled-components 로 만들어진 모듈 관리는 보통 어떤식으로 하시나요? GPT 한테 물어보니까, 아래와 같은 답변을 얻을 수 있었는데, 컴포넌트 기반 구조 components: UI 컴포넌트를 저장하는 폴더입니다 Button: 버튼 컴포넌트 index.js: 컴포넌트 로직 styles.js: styled-components 스타일 Card: 카드 컴포넌트 index.js styles.js -------------------------------------------------------- 저는 현재 프로젝트에서, Header.jsx : 헤더 컴포넌트 StyledHeader.js : 헤더 styled-components 로 관리하고 있습니다. styled-components 에 대한 모듈 관리의 일관성은 생산성 측면에서 중요하다고 생각하는데, 다들 프로젝트에서 어떤식으로 관리하시는지 궁긍합니다!
개발자
#react
#styled-components
#코드-컨벤션
#모듈-관리
답변 0
댓글 0
조회 128
2년 전 · 이양일 님의 답변 업데이트
[Nest.js] Kakao, Google Oauth 로그인 CORS 문제 ㅠㅠ (+ React)
안녕하세요 Nest.js 처음부터 공부하면서 혼자 프로젝트를 진행중인데... React(5173 port) + Nest.js(3000 port) 로 Oauth를 구현하고있습니다. ! 현재 Nest.js 쪽에서 @Get('/auth/kakao') @UseGuard(KakaoGuard) @Get('/auth/kakao/callback') @UseGuard(KakaoGuard) 로 요청을 받고있습니다. (Google도 동일) React에서 button click시에 localhost:3000/auth/kakao 로 api요청을 날리면 301 Found가 뜨게되고 'https://accounts.kakao.com/login?continue=~~~~~~~~~~' 로 리디렉션 되지만 CORS 오류가 나옵니다...... ㅠㅠㅠㅠ (구글도 구글 url로 리디렉션) 해당 도메인을 복사하여 브라우저에 직접입력하면 해당 카카오 혹은 구글 로그인 페이지로 접속이 잘되지만... 왜 프론트엔드에선 CORS가 나고있고 어떻게 해결하면 좋을지 모르겠어서 질문 남깁니다 ! 추가로 oauth에 등록하는 리디렉션 url은 http://localhost:3000/auth/kakao/callback 이며, 카카오 웹 플랫폼, 구글 자바스크립트 원본은 http://localhost:5173 입니다 !
개발자
#nest.js
#react
#oauth
답변 2
댓글 0
조회 1,138
2년 전 · 익명 님의 질문
가계부 어플리케이션 만들기 java질문
import javax.swing.*; import java.math.*; import java.util.ArrayList; import java.awt.event.*; import java.util.Scanner; import java.text.NumberFormat; import java.util.Locale; class momry extends JFrame { private ArrayList<Integer> incomeList = new ArrayList<>(); private ArrayList<Integer> expenseList = new ArrayList<>(); private boolean isIncome = false; // 초기값은 지출로 설정 public momry() { setTitle("가계부"); setSize(800, 200); JButton button1 = new JButton("추가"); JButton button2 = new JButton("제거"); JButton button3 = new JButton("수입"); JButton button4 = new JButton("지출"); button3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { JFrame frame = new JFrame(); frame.setTitle("수입입력"); frame.setSize(500, 600); JLabel label = new JLabel("수입을 입력하세요");//수입을 입력한다. frame.add(label);//라벨에 표시한다. JTextField textField = new JTextField(10); // 텍스트 필드를 생성하여 수입을 입력 받음 frame.add(textField); JButton submitButton = new JButton("확인"); submitButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ArrayList<Integer>i1=new ArrayList<Integer>(); int income = Integer.parseInt(textField.getText()); // 입력된 값을 정수로 변환 int income1 = Integer.parseInt(textField.getText()); // 입력된 값을 정수로 변환 i1.add(income); // 수입을 ArrayList에 추가 submitButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { int income = Integer.parseInt(textField.getText()); incomeList.add(income); frame.dispose(); // 입력 창 닫기 } }); frame.add(submitButton); frame.setVisible(true); } }); } NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.KOREA); }); button4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { JFrame frame1 = new JFrame(); frame1.setTitle("지출입력"); frame1.setSize(500, 600); JLabel label1 = new JLabel("지출 내용을 입력하세요");//수입을 입력한다. frame1.add(label1);//라벨에 표시한다. JTextField textField = new JTextField(10); // 텍스트 필드를 생성하여 수입을 입력 받음 frame1.add(textField); JButton submitButton1 = new JButton("확인"); submitButton1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ArrayList<Integer>i1=new ArrayList<Integer>(); int income = Integer.parseInt(textField.getText()); // 입력된 값을 정수로 변환 int income1 = Integer.parseInt(textField.getText()); // 입력된 값을 정수로 변환 i1.add(income); // 수입을 ArrayList에 추가 submitButton1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { int income = Integer.parseInt(textField.getText()); incomeList.add(income); frame1.dispose(); // 입력 창 닫기 } }); frame1.add(submitButton1); frame1.setVisible(true); } }); } }); } } class Scratch { public static void main(String[] args) { } } 이게 맞나요?
개발자
#java
#android
답변 0
댓글 0
보충이 필요해요 3
조회 138
2년 전 · 윪 님의 새로운 댓글
html form태그에서 action속성 오류
html 중에서 <form aciton="/signup" method ="POST">이 부분엔서 /signup부분에 하이퍼링크처럼 줄이 생기고 페이지를 로드했을 때 404오류가 뜨는데 혹시 어떤 부분이 잘못되서 그런지 알 수 있을까요..? 그리고 언더바가 생기는 이유도 알고 싶습니다! 구글링을 해도 제가 원하는 답이 없어서 조언 부탁드립니다 ㅜㅜ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <form action="/signup" method="POST"> <div> <label for="id">아이디</label> <input type="text" id="id" name="id" /> </div> <div> <label for="password">패스워드</label> <input type="text" id="password" name="password" /> </div> <div> <button type="submit">회원가입하기</button> </div> </form> </body> </html>
개발자
#html
#form
#frontend
#action
답변 1
댓글 1
조회 100
2년 전 · 정하승 님의 질문 업데이트
api의 첫번째 호출 이후부터 antd Button 렌더링 안되는 이슈가 있습니다.
```jsx import { Popover, Modal, Button, Image, Result } from "antd"; const [prevImg, setPrevImg] = useState(["any"]); const [loading, setLoading] = useState(false); const [removeImgFiles, setRemoveImgFiles] = useState([]); const combinePrevImages = (prevImages, newImages) => { const combinedImages = [...prevImages, ...newImages]; return combinedImages; }; useEffect(() => { const postSeg = async () => { try { const res = await axios.post( "apiurl", { filepath: filePath, clips: sortableList.map(list => `${list.seg.start}-${list.seg.end}`), frame: frameValue }, { proxy: false } ); return res.data; } catch (error) { console.error("Error posting segments:", error); return []; } finally { setLoading(false); } }; const postSegments = async () => { if (segments[0]?.start === 0 && segments[0]?.end === 0) return; if (sortableList && filePath) { setLoading(true); const res = await postSeg(); const combinedPrevImg = combinePrevImages(prevImg, res.results); setPrevImg(combinedPrevImg); console.log("Post Request Success"); } }; postSegments(); }, [filePath, segments, frameValue]); const handleModalOpen = useCallback(() => setModalOpen(true), []); const handleModalClose = useCallback(() => { setRemoveImgFiles([]); setModalOpen(false); }, []); const handleDeleteButtonClick = async () => { if (removeImgFiles.length > 0) { setPrevImg([...removeImgFiles]); setRemoveImgFiles([]); } else { const result = await showSwal({ title: "Are you sure delete?", showCancelButton: true, confirmButtonText: "Confirm", cancelButtonText: "Cancel", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33" }); if (result.isConfirmed) { setRemoveImgFiles([...prevImg]); setPrevImg([]); } const success = await Promise.all(removeImgFiles.map(deleteFiles)); return success; } } const handleRemoveFinish = async () => { if (removeImgFiles) { for (const filePath of removeImgFiles) { try { await removeFile(filePath); } catch (e) { console.log("File Remove Error", e); } } } setRemoveImgFiles([]); handleModalClose(); }; return ( <motion.div initial={{ x: width }} animate={{ x: 0 }} exit={{ x: width }} transition={mySpring} > <div style={{ fontSize: 12, padding: "0 5px", color: "var(--gray12)", display: "flex", justifyContent: "space-between", alignItems: "center" }} > <FaAngleRight title={t("Close sidebar")} size={20} className="angle-right" role="button" onClick={toggleSegmentsList} /> {header} <FaExpandArrowsAlt title={t("Image Inspection")} size={18} className="expand-arrow-alt" style={{ cursor: "pointer" }} role="button" onClick={!loading ? handleModalOpen : handleModalClose} /> <Modal title={t("Image Inspection")} centered onCancel={handleModalClose} open={modalOpen} footer={[]} width="100%" > <div className="imagecontainer"> <Button danger className="toggle-remove" onClick={handleDeleteButtonClick}> {removeImgFiles.length > 0 ? "Add" : "Remove"} </Button> {prevImg?.length > 10 && prevImg.map(img => ( <Popover key={img}> {removeImgFiles?.includes(img) ? ( <span> <Result className="result" icon={<FaSmile />} subTitle="delete" /> </span> ) : <Image key={uuidv4()} src={img} preview={{ src: img }} alt={uuidv4()} /> )} </Popover> ))} </div> <Button block onClick={handleRemoveFinish}> Finish </Button> </Modal> </div> ) ``` api 호출을 통해 frameValue 개수(여기서는 12개씩) 만큼 이미지를 렌더링 하고 있는데 두번째 호출부터는 Button이 렌더링되지 않아서 어디가 잘못됐는지 알고싶습니다.. 필요한 부분이 imagecontainer 클래스네임인 div를 렌더링 해야합니다.
개발자
#react
답변 0
댓글 0
조회 84
2년 전 · 김광민 님의 질문
react-native에서 floating button구현
react-native에서 floating button을 구현하려는데요 floating button 영역 바깥을 클릭하면 button이 닫히는 형태를 구현하려고 합니다. react에서는 position fixed하고 left 0 top 0 width 100% height 100% 하면 배경영역이 설정이되는데 react-native는 어떻게설정하나요? touchableWithoutFeedback인가? 이걸 해봐도 floating button 컴포넌트 안에서의 영역만 잡더라고요 .. 자식 컴포넌트어서 스크린의 전체 영역을 잡는 방법 알려주세요!
개발자
#react-native
답변 0
댓글 0
조회 168
2년 전 · 손정현 님의 새로운 답변
NextJS 폴더 구조 (pages 폴더 아래에 components)
안녕하세요, 회사 프로젝트 폴더 구조를 잡아야 하는데 궁금한 게 있습니다. (NextJS 버전은 12 사용 중) next.config.js에서 아래의 설정 후 pages 폴더 아래 components를 두는 구조를 사용하시는 분 계실까요? pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'] https://nextjs.org/docs/pages/api-reference/next-config-js/pageExtensions 아래와 같은 구조가 괜찮다고 판단되어 사용하려고 하는데 어떨지 의견 좀 여쭤보고 싶습니다. - apps/ --- pages/ ----- Main/ ------- page.ts ------- component.ts ------- hook.ts ------- test.ts --- shared-components/ ------- Button.ts ------- Input.ts 기존에는 pages 폴더에는 index.js만 두고 components 폴더 아래에 모든 컴포넌트들을 두는 구조로 사용 중인데, 프로젝트의 페이지가 많아지면서 특정 페이지에서만 사용하는 독립적인 컴포넌트들이 많아지다 보니 components 폴더에 있는 파일들이 구분이 잘 안됩니다. 어떤 페이지에서 사용하는 컴포넌트인지, 재사용 가능한 컴포넌트인지 명확한 구분이 어려워서 위와 같은 구조를 고민해보았는데 혹시 실제 비슷하게라도 사용 중인 곳이 있는지 궁금합니다. components 폴더 아래 page 별로 구분할 수도 있지만, 독립적인 컴포넌트는 page.ts와 같이 있는 게 조금 더 유지보수면에서 편할 것 같아 고민해보았습니다. 더 좋은 의견이 있다면 환영입니다. 어떤 폴더 구조를 사용하고 계시는지, 추천할만한 구조가 있다면 꼭 의견 부탁드리겠습니다!
개발자
#nextjs
#next.js
#folder-structure
#폴더구조
#react
답변 1
댓글 0
추천해요 2
조회 948
2년 전 · 도연 님의 댓글 업데이트
안녕하세요 NextAuth를 사용해서 구글 로그인을 구현하고 있는 중 어떻게 해결해야 할지 모르겠습니다ㅜㅜ...
아래 블로그를 참고하여 route.ts, AuthContext.tsx를 만들고 siginIn 이벤트는 사용하는 컴포넌트의 버튼 요소에 걸어주었습니다. 하지만 버튼을 누르면 404 페이지가 나오면서 url이 http://localhost:3000/api/auth/error로 뜹니다... .env 파일을 수정하고 NextAuthOptions를 사용해서 NEXTAUTH_URL, NEXTAUTH_SECRET 설정도 해보았고, 아래 코드처럼 스택 오버플로우 사이트를 참고해서 적어보기도 했지만 여전히 같은 에러가 반복됩니다... <button onClick={(e) => { e.preventDefault(); signIn('google', { callbackUrl: '/' }); }}> 구글 </button> 404 에러를 해결할 수 있는 방법이 있을까요... https://velog.io/@uni/NextAuth.js-%EA%B5%AC%EA%B8%80-%EB%A1%9C%EA%B7%B8%EC%9D%B8-Next%EB%B2%84%EC%A0%84-13.4.2
개발자
#next.js-13
#nextauth
답변 2
댓글 6
조회 717
2년 전 · 이상원 님의 새로운 댓글
Nextjs 에서 부분적으로 FOUC 가 발생하는 것 같습니다.
안녕하세요. 오늘도 Next의 심오함에 허우적대고 있는 주니어 프론트엔더 입니다. Next 에서 디자인관련 프레임워크와 라이브러리로 Ant-Design 과 Styled-components를 선택하여 프로젝트를 진행하는 중입니다. 그리고 방식은 기본 Antd의 컴포넌트를 import 하여 필요 부분만 Styled-components로 덮어 커스텀하는 방안으로 택하였습니다. 이전에 Next는 기본적으로 서버에서 html을 생성하려는 경향이 있기에 css-in-js는 가끔가다 hydrating 과정으로 인해 스타일이 적용되지 않는 부분만 먼저 보여진다고 들었습니다. 그래서 현재 next 13을 사용하고 있어 (app routing 방식이 아닌 구 버전인 pages routing 방식을 사용하고 있습니다.) next.config.js에서 styled-components를 인식하게 도와주는 설정 코드를 작성하였습니다. - 해당 설정은 최하단에 첨부하였습니다. 문제는 FOUC 관련 이슈가 일부 컴포넌트에만 나타납니다. Button컴포넌트와 Input컴포넌트를 위와 같은 스타일 방식으로 제작을 하였는데, 페이지가 리프레쉬가 되면 Button 컴포넌트는 기존 styled-components의 커스텀을 유지하지만, Input 컴포넌트는 styled-comopents의 커스텀을 입히기 전의 Ant-Design의 기본 디자인으로 렌더링되는 문제가 발생하였습니다. 그래서 제가 진행했던 FOUC 관련 설정에 문제가 있는지 궁금하여 질문을 드리려고 합니다!
개발자
#next.js
#next.config.js
#ant-design
#styled-components
답변 1
댓글 1
추천해요 1
조회 370
2년 전 · 엄홍재 님의 새로운 답변
nextjs 초보 fetch & DELETE 동작이 안되요.
app/api/list/[id]/route.js export async function DELETE(request, {params}) { const id = params.id; const {searchParams} = request.nextUrl; const sort = searchParams.get('sort'); return NextResponse.json({message: 'test', id, sort}); } 위처럼 되어있고, app/list/ListItem.js <button className="blue"> <span onClick={() => { fetch('/api/list/' + post._id, { method: 'DELETE' }).then(() => { console.log('call delete'); }); }}>삭제</span> </button> 위 처럼 코드를 작성했는데, DELETE route가 실행이 되지 않습니다. 어떻게 해야 하나요?... POST의 경우에는 아래와 같은데 동작이 잘 되서요.. app/write/route.js export async function POST(request) { const data = await request.formData(); let body = Object.fromEntries(data); const db = (await connectDB).db('exam'); await db.collection('post').insertOne(body); return new Response('POST'); } app/write/page.js <form action="/api/write" method="POST"> <input type="text" name="title" placeholder="제목"/> <br/> <input type="text" name="content" placeholder="내용"/> <button type="submit">NEW</button> </form>
개발자
#nextjs
답변 1
댓글 0
조회 176
2년 전 · NickSoon 님의 새로운 댓글
Javascript 에서 setTimeout으로 redirect 후 뒤로가기 했을때 이전 페이지가 아닌 전 전 페이지로 돌아가는 이유
크롬 브라우저에서 스크립트 setTimeout으로 이동 후에 이동 된 페이지에서 브라우저 뒤로가기 버튼을 누르면 이전 페이지가 아니라 그 전 페이지로 이동 되는 문제가 있는데 어떻게 해결 할 수 있을까요? 현재 a, b, c html 3개 파일이 있고 a는 누르면 b로 이동하는 버튼 하나를 가지고 있습니다. b는 아무 동작 없이 setTimeout으로 cl로 이동합니다. c는 아무 코드가 없고 코드를 작성할 수 없습니다. a에서 버튼을 눌러서 b로 location.href 로 이동 후 b에서 setTimeout(() => {location.replace('c.html')}, 5000) 으로 c로 이동 한 뒤 c 에서 뒤로가기를 하면 b가 아닌 a로 이동 됩니다. (b에서 유저가 클릭이나 다른 액션을 하면 c에서 뒤로가기 시 b로 오지만 아무런 액션이 없을 때 a로 돌아갑니다.) c를 수정할 수 없을 떄 b를 수정해서 이 이슈를 해결하는 방법이 있을까요? stackoverflow (https://stackoverflow.com/questions/76197884/after-redirect-from-settimeout-chrome-edge-back-button-doesnt-work-well) 에도 비슷한 이슈가 있는데 여기도 해결된 답은 없어서 여기에 질문을 남겨봅니다.
개발자
#javascript
답변 1
댓글 3
조회 340
2년 전 · 커리어리 AI 봇 님의 새로운 답변
리액트로 스프링과 웹소켓 채팅방을 구현했는데 자동 랜더링이 안됩니다..
안녕하세요! 현재 웹소켓으로 스프링과 채팅기능을 구현중에 있습니다 채팅방에서 채팅을 보내고 받는 건 가능한 상태인데 같이 채팅방에 입장해서 A가 B한테 보냈을 때 B가 페이지를 새로고침 하지 않으면 채팅이 자동 랜더링이 되지 않는 상황인데 여러 방법을 참고하고 해봤지만.. 성공하지 않았습니다 어떻게 풀어나가야 할지 잘 모르겠습니다 ㅠㅠ 코드가 길지만 ... 혹시 답변이 가능할까해서 참고해봅니다 좋은 키워드도 추천해주시면 감사하겠습니다!!... export const ChatRoomPage = () => { //메뉴 모달 const [isModalOpen, setIsModalOpen] = useState(false); const [isExitModalOpen, setIsExitModalOpen] = useState(false); const [backgroundPosition, setBackgroundPosition] = useState('static'); const location = useLocation(); const params = location.pathname; const segments = params.split('/'); const RoomUniqueId = segments[4]; const RoomId = segments[5]; const [messageData, setMessageData] = useState([]); const [messageList, setMessageList] = useState([]); const [message, setMessage] = useState(''); const accesskey = Cookies.get('Access_key'); // 채팅방 입장시 안내 문구 기능 const [showModal, setShowModal] = useState(false); const client = useRef({}); useEffect(() => { console.log('유즈이펙트 쉴행'); setShowModal(true); connect('L'); return () => disconnect(); }, []); const connect = type => { client.current = new StompJs.Client({ brokerURL: 'ws://222.102.175.141:8081/ws-stomp', connectHeaders: { Access_key: `Bearer ${accesskey}`, }, debug: function (str) { console.log('str ::', str); }, onConnect: () => { if (type === 'L') { subscribe(); publish(); } else { subscribe1(); publish1(); } }, }); client.current.webSocketFactory = function () { return new SockJS('http://222.102.175.141:8081/ws-stomp'); }; client.current.activate(); return () => disconnect(); }; const subscribe = () => { client.current.subscribe(`/sub/chat/messageList/${localStorage.memberUniqueId}`, message => { // console.log('messageData11 : ', JSON.parse(`${message.body}`)); setMessageData(JSON.parse(`${message.body}`)); const data = JSON.parse(`${message.body}`); setMessageList(data.data.chatMessageList); }); }; const publish = () => { client.current.publish({ destination: `/pub/chat/messageList/${localStorage.memberUniqueId}`, body: JSON.stringify({ chatRoomId: RoomId, chatRoomUniqueId: RoomUniqueId, page: 0, }), }); }; const closeModal = () => { setIsModalOpen(false); setBackgroundPosition('static'); }; const openModal = () => { setIsModalOpen(true); setBackgroundPosition('fixed'); }; const handleBackdropClick = e => { console.log('e ::', e); if (e.target === e.currentTarget) { closeModal(); } }; const ExitopenModal = () => { setIsExitModalOpen(true); }; const ExitcloseModal = () => { setIsExitModalOpen(false); }; const ReportButtonHandler = () => { alert('곧 업데이트 예정입니다!'); }; // 채팅 보내기 const sendMessage = message => { console.log('message :: ', message); connect(); setMessage(''); return () => disconnect(); }; const subscribe1 = () => { client.current.subscribe(`/sub/chat/message/${RoomUniqueId}`, message => { setMessageData({ ...messageList, message }); }); }; const publish1 = () => { client.current.publish({ destination: `/pub/chat/message/${RoomUniqueId}`, body: JSON.stringify({ memberId: `${localStorage.memberId}`, memberName: `${localStorage.memberName}`, memberUniqueId: `${localStorage.memberUniqueId}`, memberProfileImage: `${localStorage.profileImage}`, chatRoomId: RoomId, chatRoomUniqueId: RoomUniqueId, message: message, }), }); }; const disconnect = () => { client.current.deactivate(); }; console.log('messageList :: ', messageList); return ( <> <div style={{ width: '100%', height: '100%', position: backgroundPosition, }} > <Background> <Topbar> <Link to={`${PATH_URL.PARTY_CHAT}/${localStorage.memberUniqueId}`}> <TopBackDiv> <LeftBack /> </TopBackDiv> </Link> <TopbarName>모임이름</TopbarName> <ModalBtn onClick={() => { openModal(); }} > <RoomMenuIcon /> </ModalBtn> </Topbar> <Container> <Contents> <ParticipantDiv>ㅇㅇㅇ님이 참여했습니다.</ParticipantDiv> {messageList?.map((data, index) => { return ( <OtherDiv key={index}> <div style={{ position: 'relative', }} > <OtherImg> <OtherProfile> <img src={data.memberProfileImage} alt="profile" style={{ width: '100%', height: '100%', borderRadius: '8px', }} /> </OtherProfile> <OtherHostIcon> <PartHostIcon /> </OtherHostIcon> </OtherImg> <OthertInfo> <OtherName>{data.sender}</OtherName> <OtherContents> <OtherChatText>{data.message}</OtherChatText> <OtherChatTime>12:19 pm</OtherChatTime> </OtherContents> </OthertInfo> </div> </OtherDiv> ); })}
개발자
#채팅
#웹소켓
#채팅기능
답변 2
댓글 0
조회 607
2년 전 · 이재찬 님의 답변 업데이트
react component 나눌때… 어느게 맞을까요?
안녕하세요 리액트랑 nextjs 를 사용해서 코딩중인데 한가지 의문점이 들어서 질문남깁니다 ! 하나의 페이지 templates에서 <Title /> <SearchForm /> <Content /> <ButtonBox /> 이런식으로 세세하게 하나씩 일일이 컴포넌트로 나누는게 좋은것인지... 아니면 적당히 하나의 기준으로 나누는게 좋은것인지 혹시 그렇다면 어떤기준으로 나누어야 하는지... 궁금증이 들어서 이렇게 질문남깁니다 ㅜㅠ 읽어주셔서 감사합니다 !
개발자
#react
#next.js
#frontend
답변 1
댓글 0
조회 220
2년 전 · 커리어리 AI 봇 님의 새로운 답변
리액트 라우팅을 이렇게 하는게 맞는지 모르겠습니다..
안녕하세요 독학 하고 있는 학생입니다.. 작은 프로젝트로 혼자서 홈페이지를 만들고 있습니다. 우선 로그인 페이지부터 만들고 있는데, 로그인페이지에는 아이디찾기 비밀번호찾기 등등 많은 페이지가 들어가더라구요.. 그래서 리액트 라우터를 공부하고 적용시켰습니다. 코드는 아래와 같습니다. 이렇게 해도 제가 원하는대로 로그인페이지에서 아이디 찾기 페이지로 이동하고 하는 것은 맞는데 App.js에 이렇게 주저리주저리 원하는 것을 다 넣어두면 나중에 전체적으로 페이지를 완성시켰을 때 App.js에 너무 방대한 내용이 들어가지않나..? 싶더라구요.. 그래서 중첩된 라우트도 찾아서 공부했는데 크흡..암만해도 적용이 안됩니다.. 그래서 그냥 이대로 홈페이지를 계속 만들어도 되는지,,아니면 저의 고민을 해결할 방법이 중첩된 라우트가 맞는지 알고싶습니다.. 맞다면 다시 공부해야겠죠 ㅜ.. 답변부탁드립니다 (_ _) import "./App.css"; import { Route, Routes } from "react-router-dom"; import WigTemplate from "./components/wigtemplate"; import FindId from "./components/findId"; import FindPwd from "./components/findPwd"; import SignIn from "./components/signIn"; import NotFound from "./components/notFound"; function App() { return ( <Routes> <Route path="/" element={<WigTemplate />} /> <Route path="/findId" element={<FindId />} /> <Route path="/findPwd" element={<FindPwd />} /> <Route path="/signIn" element={<SignIn />} /> <Route path="*" element={<NotFound />} /> </Routes> ); } export default App; ---------------------------------------------------------- import React from "react"; import styled from "styled-components"; import WigLoginButton from "./wigLoginButton"; import WigHeader from "./wigHeader"; import WigInput from "./wigInput"; import WigFind from "./wigFind"; const WigTemplateContainer = styled.div` height: 100vh; display: flex; align-items: center; justify-content: center; `; const WigTemplateBlock = styled.div` width: 500px; height: 600px; background: #d0ebff; border-radius: 80px; `; function WigTemplate() { return ( <WigTemplateContainer> <WigTemplateBlock> <WigHeader></WigHeader> <WigInput></WigInput> <WigFind></WigFind> <WigLoginButton></WigLoginButton> </WigTemplateBlock> </WigTemplateContainer> ); } export default WigTemplate; ------------------------------------------------------- import React from "react"; import styled from "styled-components"; import { RxDividerVertical } from "react-icons/rx"; import { Link } from "react-router-dom"; const FindBlock = styled.div` display: flex; justify-content: center; `; const ABlock = styled(Link)` color: black; font-size: 0.9rem; text-decoration: none; `; function WigFind() { return ( <FindBlock> <ABlock to="/findId">아이디 찾기</ABlock> <RxDividerVertical style={{ marginTop: "3px" }} /> <ABlock to="/findPwd">비밀번호 찾기</ABlock> <RxDividerVertical style={{ marginTop: "3px" }} /> <ABlock to="/signIn">회원가입</ABlock> </FindBlock> ); } export default WigFind;
개발자
#react
답변 2
댓글 3
조회 483
2년 전 · 커리어리 AI 봇 님의 새로운 답변
개발자 선생님들 도와주세요..
안녕하세요 초보개발자입니다 지금 구글로 코드 복붙하며 게시판 수정중인데 아예 똑같이 복붙 하였는데 저는 왜 이런 식으로 나올까요 도와주세요 .. ㅠㅠ 프로젝트 발표가 코앞인데.. 1번째사진은 작성자의 사진이고 2번째 사진이 제 출력 화면입니다... 코드는 댓글에 적어두겠습니다..도와주세요.. ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!--부트스트랩은 어떤device로 접속하더라도 해상도에 맞게 알아서 설정되는 탬플릿이다. --> <meta name="viewport" content="width=device-width" , inital-scale="1"> <!--스타일시트를 참조, 주소는 css안에 부트스트랩.css--> <link rel="stylesheet" href="css/bootstrap.css"> <title>JSP 게시판 웹 사이트</title> </head> <body> <!-- 네비게이션 구현 네비게이션이라는 것은 하나의 웹사이트의 전반적인 구성을 보여주는 역할 --> <nav class="navbar navbar-default"> <!-- header부분을 먼저 구현해 주는데 홈페이지의 로고같은것을 담는 영역이라고 할 수 있다. --> <div class="navbar-header"> <!-- <1>웹사이트 외형 상의 제일 좌측 버튼을 생성해준다. data-target= 타겟명을 지정해주고--> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-exmaple="false"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <!-- 여긴 웹페이지의 로고 글자를 지정해준다. 클릭 시 main.jsp로 이동하게 해주는게 국룰 --> <a class="navbar-brand" href="main.jsp">JSP 게시판 웹 사이트</a> </div> <!-- 여기서 <1>에만든 버튼 내부의 데이터 타겟과 div id가 일치해야한다. --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <!-- div 내부에 ul은 하나의 어떠한 리스트를 보여줄때 사용 --> <ul class="nav navbar-nav"> <!-- 리스트 내부에 li로 원소를 구현 메인으로 이동하게만들고--> <li><a href="main.jsp">메인</a></li> <!-- 게시판으로 이동하게 만든다. --> <li><a href="bbs.jsp">게시판</a></li> </ul> <!-- 리스트 하나 더 생성 웹페이지 화면에서 우측 부분--> <ul class="nav navbar-nav navbar-right"> <!-- 원소를 하나 구현해 준다. 네비게이션 우측 슬라이드메뉴 구현 --> <li class="dropdown"> <!-- 안에 a태그를 하나 삽입한다. href="#"은 링크없음을 표시한다. --> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">접속하기<span class="caret"></span></a> <!--접속하기 아래에 드랍다운메뉴 생성 --> <ul class="dropdown-menu"> <!-- li class="active" 현재 선택된 홈페이지를 의미 --> <li><a href="login.jsp">로그인</a></li> <li class="active"><a href="join.jsp">회원가입</a></li> </ul> </li> </ul> </div> <!-- 네비게이션 바 구성 끝 --> </nav> <!-- 하나의 컨테이너처럼 감싸주는 역할 --> <div class="container"> <div class="col-lg-4"></div> <!-- 회원가입 폼은 위의 양식은 일치하며, 이제 내부 폼만 바꿔준다. --> <div class="col-lg-4"> <div class="jumbotron" style="padding-top: 20px;"> <!-- 양식 삽입 post는 회원가입이나 로그인같이 어떠한 정보값을 숨기면서 보내는 메소드/ 로그인 Action페이지로 정보를보내겠다--> <form method="post" action="joinAction.jsp"> <!-- 회원 가입에 맞게 위에 액션은 joinAction페이지로 밑에 제목은 회원가입 화면으로 변경 --> <h3 style="text-align: center;">회원가입 화면</h3> <div class="form-group"> <!-- 회원 가입에서도 userID or userPassword는 동일하게 가져가고, 회원가입에 필요한 나머지 속성추가 --> <input type="text" class="form-control" placeholder="아이디" name="userID" maxlength="20"> </div> <div class="form-group"> <input type="password" class="form-control" placeholder="비밀번호" name="userPassword" maxlength="20"> </div> <!-- userName 추가 --> <div class="form-group"> <input type="text" class="form-control" placeholder="이름" name="userName" maxlength="20"> </div> <!-- 성별 선택 추가 --> <div class="form-group" style="text-align: center;"> <!-- 버튼 공간을 따로 마련해준다.(남,녀) --> <div class="btn-group" data-toggle="buttons"> <!-- 선택이 된곳에 표시를 하는 active를 설정해준다. --> <label class="btn btn-primary active"> <input type="radio" name="userGender" autocomplete="off" value="남자" checked>남자 </label> <label class="btn btn-primary"> <input type="radio" name="userGender" autocomplete="off" value="여자" checked>여자 </label> </div> <!-- 성별 선택부분 완료 --> </div> <!-- email 작성부분 구현 --> <div class="form-group"> <!-- placeholder는 아무런 입력이 없을때 띄워주는 값 --> <input type="email" class="form-control" placeholder="이메일" name="userEmail" maxlength="20"> </div> <!-- 버튼 또한 회원가입으로 value변경 --> <input type="submit" class="btn btn-primary form-control" value="회원가입"> </form> </div> </div> <div class="col-lg-4"></div> </div> <!-- 애니메이션을 담당하게 될 자바스크립트 참조 --> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <!-- 특정홈페이지에서 제이쿼리 호출 --> <script src="js/bootstrap.js"></script> </body> </html>
개발자
#jsp
#java
답변 2
댓글 4
보충이 필요해요 3
조회 387
2년 전 · 커리어리 AI 봇 님의 새로운 답변
<input type="file" multiple> 관련하여..
<template> <div> <input ref="fileInput" type="file" accept="image/*" multiple="true" @change="handleFileInputChange" /> <button @click="openFileInput" class="select-button"> Select Images </button> <div class="preview-container"> <div v-for="file in selectedFiles" :key="file.lastModified" class="preview-item" > <p>{{ file.name }}</p> <div class="preview"> <img :src="previewImage" style="width: 100px" /> </div> <!-- <img :src="URL.createObjectURL(file)" /> --> <button @click="removeFile(file.lastModified)" class="remove-button"> X </button> </div> </div> </div> </template> <script setup lang=“ts”> const fileInput = ref() const selectedFiles: Ref<Array<any>> = ref([]) const openFileInput = () => { fileInput.value.click() } const previewImage = ref<any>('') const handleFileInputChange = e => { // const files = Array.from(fileInput.value.files) const files = e.target.files console.log('files', files) selectedFiles.value.push(...files) console.log('add selectedFiles', selectedFiles.value) for (let i = 0; i < selectedFiles.value.length; i++) { const file = selectedFiles.value[i] console.log('handleFileInputChange_ file', selectedFiles.value[i]) const reader = new FileReader() reader.onload = e => { previewImage.value = e.target.result } reader.readAsDataURL(file) } } const removeFile = lastModified => { selectedFiles.value = selectedFiles.value.filter( file => file.lastModified !== lastModified ) console.log('remove selectedFiles', selectedFiles.value) } </script> 모바일에서 카메라로 찍은 여러장의 사진들이 각각의 썸네일로 노출되어야함 현재는 최신 사진으로 엎어치기 됨.. 예) 'A B C D' 의 사진이 나와야하는데 현재는 A 찍고 B찍으면 'B B' 가 됨 C찍으면 'C C C'가 됨 도와주세요!
개발자
#input
#type='file'
#vue3
#multiple
#preview
답변 3
댓글 0
보충이 필요해요 1
조회 173
2년 전 · 커리어리 AI 봇 님의 새로운 답변
안드로이드 블루투스 권한 요청
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) if (bluetoothAdapter == null) { Toast.makeText(this, "블루투스를 지원하지않는 기기입니다", Toast.LENGTH_SHORT).show() finish() } setContentView(binding.root) binding.btn3.setOnClickListener { checkBlueToothConnectPermission() } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == PERMISSION_REQUEST_BLUETOOTH_CONNECT) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 권한이 허용된 경우 처리할 코드 } else { Toast.makeText(this, "BLUETOOTH_CONNECT 권한이 필요합니다.", Toast.LENGTH_SHORT).show() } } } fun checkBlueToothConnectPermission() {//앱에서 블루투스 연결 권한이 있는지 확인하고 없는경우 권한을 요청하는데 사용됨 if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED ) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.BLUETOOTH_CONNECT)){ AlertDialog.Builder(this) .setTitle("권한 요청") .setMessage("블루투스 연결 권한이 필요합니다.") .setPositiveButton("확인") { _, _ -> ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.BLUETOOTH_CONNECT), PERMISSION_REQUEST_BLUETOOTH_CONNECT ) } .setNegativeButton("취소", null) .show() }else{ ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.BLUETOOTH_CONNECT), PERMISSION_REQUEST_BLUETOOTH_CONNECT ) } } } 버튼을 눌러서 권한요청을하는데 거부를 했다면 다시한번 눌렀을때 다시권한요청을 하고싶은데 어떻게 하시는지 아시는분 계신가요ㅜ 한번 거부가 되면 요청다이얼로그가 뜨지않는걸 알고 대화상자로 처리할라고하는데 대화상자도 안뜨네요 ㅜ
개발자
#안드로이드
#권한처리
#블루투스
답변 2
댓글 1
조회 376
2년 전 · 커리어리 AI 봇 님의 새로운 답변
input type="file" 사용법
<template> ...(생략) <label for="camera" class="btn btn-purple btn-square btn-lg col-6 right-border" v-if=" imageYn === 'N' && imageYn === null" > 사진 촬영 </label> //class가 조건에 따라 달라짐. <label for="camera" class="btn btn-secondary btn-square btn-lg col-6 right-border" v-if=" imageYn === 'Y' " > 사진 촬영 </label> <input type="file" id="camera" accept="image/*" capture="camera" style="display: none" @change="onFileSelected" /> <button class="btn btn-secondary btn-square btn-lg col-6 left-border" @click="rightButton" v-if="status === '방전 시작'" > 방전 완료 </button> ...(생략) </template> <script setup lang="ts"> const imageYn:string = ref('') console.log('2', imageYn.value) //사진촬영 const onFileSelected = event => { const file = event.target.files[0] if (file) { imageYn.value = 'Y' console.log('1') console.log('imageYn', imageYn.value) console.log('yes file', file) } else { imageYn.value = 'N' console.log('imageYn', imageYn.value) console.log('no image') } } </script> 결과 사진 찍기 전 -> imageYn ='N' 이거나 null일 경우-> class에 btn-purple 적용 카메라를 실행하여 사진을 찍었을 경우 -> imageYn='Y' -> class에 btn-secondary 적용 / disable처리도 되야함 문제점 사진촬영 후 console 1번은 'Y' 찍히되 2번엔 null입니다. 도와주세요!
개발자
#자바스크립트
#vue3
#compositionapi
#input
#type="file"
답변 1
댓글 0
조회 378
2년 전 · 커리어리 AI 봇 님의 새로운 답변
headlessui 의 listbox 써보신 분 계신가요?
공식문서 - https://headlessui.com/react/listbox listbox를 사용하는데 select 항목 안에도 button을 만들어서 option을 선택할 때가 아닌 button을 클릭할 때 select를 적용하고 싶은데요. button을 누르면 select항목을 닫고 싶습니다. 어떻게 해야 닫을 수 있나요? 지금은 버튼눌러도 아무 동작을 안합니다 ㅠ
개발자
#react
#headlessui
#listbox
답변 2
댓글 0
조회 446
2년 전 · 커리어리 AI 봇 님의 새로운 답변
하이브리드 앱 카메라 연동
<input type="file" id="camera" accept="image/*" capture="camera" /> 추가하여 카메라 앱을 열려고 하는데 어떻게 적용해야할까요?ㅠㅠ도와주세요 <template> <button @click="camera"> 사진 촬영 </button> </template> <script setup lang="ts"> const camera = () => { console.log('카메라연동') } </script>
개발자
#vue3
#하이브리드앱
#카메라연동
답변 1
댓글 0
조회 638