Giter VIP home page Giter VIP logo

ready-act's People

Contributors

hongdayeong avatar ingbinsee avatar jellyjoji avatar seojinman avatar

ready-act's Issues

[R09M App Backlog] 인증 페이지 - 회원가입

내용

🛠️ 회원 정보 등록

  • 클라이언트 SDK를 활용하여 기재한 회원 정보를 pocketHost에 POST
  • 회원가입이 정상적으로 진행되었을 때 로그인 페이지로 이동

달성도

9월 13일

  • 클라이언트 SDK를 활용하여 데이터 쓰기(Create)
  • 마크업 및 스타일링

[R09M App] CreateRoom 페이지 - value 이슈

내용

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components

Creator.jsx 파일에 위와 같은 오류메세지가 계속 발생하여 input 요소에 문제점을 분석하였습니다.

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] CreateRoom 페이지 - .current.dataset 의 값을 읽어오지 못하는 이슈

내용

PaymentToggleButton.jsx 에서 context 를 사용하여 데이터를 createRoomForm 으로 전달한뒤 createRoomForm.payment 로 불러와서 를 .current.datasetdata-payment={isToggled ? '계좌 이체' : '만나서 결제'} 를 호출하니 에러가 뜨며 dataset 을 읽어들이지 못합니다.

아래와 같이 토글버튼을 누르면 true와 false 값을 출력해내지만 dataset 을 찾지 못한다는 에러가 뜨씁니다. context 를 사용해서 불러온 createRoomForm.payment 값에 dataset 를 적용시켜 사용하려고 합니다.

.current.dataset 를 어느 부분에 적용시켜 dataset를 값으로 읽어올수가 있을까요?

import { AppContext } from '@/App';
import { useContext, useEffect, useState } from 'react';

function PaymentToggleButton({ value = "false", labelClassName, ...restProps }) {
  const { updateCreateRoomForm } = useContext(AppContext);
  const [isToggled, setToggled] = useState(value);

  useEffect(() => {
    updateCreateRoomForm('payment', isToggled);
    console.log(isToggled);
  }, [isToggled]);

  return (
    <>
      <label htmlFor="payment" className={labelClassName}>
        지불 방법
      </label>
      <div id="payment" className="bg-greenishgray-200 w-full p-2 rounded-lg">
        <button
          value={isToggled}
          type="button"
          className="w-full rounded-lg "
          onClick={() => setToggled(!isToggled)}
          data-payment={isToggled ? '계좌 이체' : '만나서 결제'}
          {...restProps}
        >
          <div
            className={`flex w-full ${isToggled ? 'items-center' : ''}`.trim()}
          >
            <div
              className={`w-1/2 ${!isToggled ? 'shadow-lg bg-white rounded-lg' : ''
                }`.trim()}
            >
              만나서 결제
            </div>
            <div
              className={`w-1/2 ${isToggled ? 'shadow-lg bg-white rounded-lg' : ''
                }`.trim()}
            >
              계좌 이체
            </div>
          </div>
        </button>
      </div>
    </>
  );
}

export default PaymentToggleButton;

다음은 토글 버튼의 UI 구현 부분에 대한 질문입니다.

<button
          value={isToggled}
          type="button"
          className="w-full rounded-lg "
          onClick={() => setToggled(!isToggled)}
          data-payment={isToggled ? '계좌 이체' : '만나서 결제'}
          {...restProps}
        >
          <div
            className={`flex w-full ${isToggled ? 'items-center' : ''}`.trim()}
          >
            <div
              className={`w-1/2 ${!isToggled ? 'shadow-lg bg-white rounded-lg' : ''
                }`.trim()}
            >
              만나서 결제
            </div>
            <div
              className={`w-1/2 ${isToggled ? 'shadow-lg bg-white rounded-lg' : ''
                }`.trim()}
            >
              계좌 이체
            </div>
          </div>
        </button>

위의 토글 버튼의 마크업을 아래의 코드와 같이 개선시켜보았습니다.

코드를 조금 간결하게 만들수있었지만 반복되는 부분을 더 줄여줄수있을지 궁금합니다.

혹시 반복되는 w-1/2 ${isToggled ? 'shadow-lg bg-white rounded-lg' : ''} 이 css 부분을 더 줄여줄수있는 연산식이 있을지 여쭤봅니다!
``jsx
<button
type="button"
className="w-full rounded-lg flex items-center"
onClick={() => setToggled(!isToggled)}
data-payment={isToggled ? '계좌 이체' : '만나서 결제'}
{...restProps}
>
<div className={w-1/2 ${!isToggled ? 'shadow-lg bg-white rounded-lg' : ''}}>
만나서 결제

<div className={`w-1/2 ${isToggled ? 'shadow-lg bg-white rounded-lg' : ''}`}>
계좌 이체



## 참고 이미지 (선택)
![image](https://github.com/FRONTENDSCHOOL6/ready-act/assets/74365275/7f2f2063-f681-4da5-b8bc-9671b9891fcc)


`※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부`

[R09M App Backlog] 게시물 상세 페이지(Detail) - 게시물 상세 정보 화면에 렌더링

내용

🛠️
게시물 상세 페이지(Detail 페이지)를 포켓 호스트와 연결하여 게시물의 상세 정보들을 불러와 화면에 렌더링 및 스타일링

시안 이미지

🌃 작업할 이미지를 첨부해 주세요.
image

달성도

🕒 100% 달성 시까지 얼마나 걸릴지 예상 작업 시간을 작성해 주세요.
9월 8일 작업 시작
9월 13일 작업 완료 예상
※ 댓글에 진행사항을 업데이트

[R09M App] CreateRoom 페이지 - Kakao Map 에서 value 값을 기억해야할때 value={data}의 위치 이슈

내용

Kakao Map 에서 찍은 주소값을 updateCreateRoomForm 에 pickUp 항목으로 저장시켰습니다, 해당 페이지를 벗어나도 vlaue 값이 기억되도록 useState(value) 를 주고 value=createRoomForm.meetingPoint} 를 CreateRoom 의 MeetingPorint컴포넌트에 적용시켰습니다. value 값을 기억해야할때 를 적용시킬 속성의 위치가 여기가 맞을까요 ? (굵은 글자 표시)

<div className="hAddr flex">
          {/* 여기 */}
          <span readOnly value={data || ""} id="centerAddr" className='p-4'>
            {data}
          </span>

Location.jsx 의 전체 코드를 공유합니다.

import arrowLeft from '@/assets/icons/arrowLeft.svg';
import { useContext, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import './Location.module.css';
import { AppContext } from '@/App';
import Button from "../../components/Button";

const { kakao } = window;

function Location({ value }) {
  const { updateCreateRoomForm } = useContext(AppContext);

  const [data, setData] = useState(value);

  useEffect(() => {
    const mapContainer = document.getElementById('map'), // 지도를 표시할 div
      mapOption = {
        center: new kakao.maps.LatLng(37.57157200866145, 126.9763416696016), // 지도의 중심좌표
        level: 4,
      };

    const map = new kakao.maps.Map(mapContainer, mapOption);

    const geocoder = new kakao.maps.services.Geocoder();

    const marker = new kakao.maps.Marker(),
      infowindow = new kakao.maps.InfoWindow({ zindex: 1 });

    searchAddrFromCoords(map.getCenter(), displayCenterInfo);

    kakao.maps.event.addListener(map, 'click', function (mouseEvent) {
      searchDetailAddrFromCoords(mouseEvent.latLng, function (result, status) {
        if (status === kakao.maps.services.Status.OK) {
          let detailAddr = !!result[0].address.address_name
            ? result[0].address.address_name
            : '위치정보를 불러올수없음';
          setData(detailAddr);

          const content = '<div className="bAddr">' + detailAddr + '</div>';

          marker.setPosition(mouseEvent.latLng);
          marker.setMap(map);

          infowindow.setContent(content);
          infowindow.open(map, marker);
        }
      });
    });

    kakao.maps.event.addListener(map, 'idle', function () {
      searchAddrFromCoords(map.getCenter(), displayCenterInfo);
    });

    function searchAddrFromCoords(coords, callback) {
      geocoder.coord2RegionCode(coords.getLng(), coords.getLat(), callback);
    }

    function searchDetailAddrFromCoords(coords, callback) {
      geocoder.coord2Address(coords.getLng(), coords.getLat(), callback);
    }

    function displayCenterInfo(result, status) {
      if (status === kakao.maps.services.Status.OK) {
        const infoDiv = document.getElementById('centerAddr');

        for (let i = 0; i < result.length; i++) {
          if (result[i].region_type === 'H') {
            infoDiv.innerHTML = result[i].address_name;
            break;
          }
        }
      }
    }
  }, []);

  useEffect(() => {
    updateCreateRoomForm('meetingPoint', data);

  }, [data]);


  return (
    <div className="h-full">
      <div className="relative h-12">
        <p className="text-center py-3">지도에서 위치 확인</p>

        <Link to="/createroom">
          <img src={arrowLeft} alt="뒤로 가기" className="absolute top-3" />
        </Link>
      </div>

      <div className="map_wrap">
        <div id="map" className="w-full h-[420px]"></div>
        <div className="hAddr flex">
          {/* <input readOnly value={data || ""} id="centerAddr" className='p-4' /> */}
          <span id="centerAddr" className='p-4'>
            {data}
          </span>

          <Link to="/createroom" className="bg-white w-full absolute max-w-xl bottom-0 p-4 drop-shadow-2xl">
            <Button type="submit" className="activeButton lgFontButton w-full ">
              이 위치로 설정
            </Button>
          </Link>

        </div>
      </div>
    </div>
  );
}

export default Location;

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 프로필 페이지 - 로그아웃 / 회원탈퇴 기능

내용

🛠️ 로그아웃 / 회원탈퇴 기능 구현

  • 로그아웃 시 로컬스토리지에 등록된 정보 제거
  • 회원탈퇴 시 pocketHost에 등록된 정보 삭제

시안 이미지

없음

달성도

9월 16일

  • localStorage.removeItem을 활용하여 로그아웃 시 보관된 정보 제거
  • pocketHost를 활용하여 회원탈퇴 시 유저 정보 삭제

[R09M App] CreateRoom 페이지 - Map API 분리 이슈

내용

kakao Map API 를 구현한 다음 component 로 독립적인 페이지인 Location.jsx 로 분리하는 작업을 하고있습니다. 만날 장소 등록하기를 클릭하면 새 페이지에서 열리는 방식으로 분리했을때 지도에서 새 페이지에서 지도를 눌러 얻은 결과값을 현재 작업 중인 createRoom page 로 불러와야합니다.
해당 결과값을 불러와야 하는 방식에 대해 논의드립니다.

참고 이미지 (선택)

아래와 같이 지도에 찍힌 주소 결과값을 아래와 같이 메인 createRoom 페이지의 form data 로 불러와서 입력하는 방식에 대해 질문 드립니다.
Screenshot 2023-09-14 at 3 58 24 PM
Screenshot 2023-09-14 at 4 05 09 PM

(참고)
사용한 kakao map api 첨부
https://apis.map.kakao.com/web/sample/coord2addr/

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 검색 페이지 - 상품 검색 기능 구현

내용

🛠️ 키워드 입력 시 키워드가 포함된 상품 렌더링

시안 이미지

없음

달성도

9월 18일

  • 클라이언트 SDK를 활용하여 데이터 필터링
  • 검색어의 value에 따라 필터값 변경
  • 필터링된 상품 화면에 렌더링

[R09M App] 채팅 페이지 - 구현 방식 이슈

내용

문제 상황

image

  • 이 코드를 주석 처리 했을 때는 렌더링이 되는데 이 주석 처리를 지우면 렌더링이 되지 않는 모습을 볼 수 있습니다. (렌더링이 되는지 확인을 위해 테스트 글자를 입력했는데, 주석 처리 하면 나타나고,주석 처리를 풀었을 때는 나타나지 않았습니다.)
  • 참고한 공식 문서 코드입니다.

image

정리하자면

  1. 포켓호스트에 어떻게 데이터를 쌓아야 하는지
  2. 리얼타임을 활용하여 실시간으로 채팅 기능 구현을 어떻게 하는지
  3. 관계성을 어떻게 가져가야 하는지
  4. 코드에서 action 이랑 message 가 무엇을 의미하는지 아직 이해가 되지 않습니다.

참고 이미지 (선택)

16조의 채팅 기능 시안 입니다.
게시글_채팅_주문 금액(주문 받기)

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] CreateRoom 페이지 - Creator form 이슈

내용

form data 에서 Creator 생성자 정보를 불러와서 표기해주려고 합니다.

  • local storage 아이디정보를 getItem JSON.parse 로 불러와서 id 랑 local storage 일치 여부를 확인 후 로그인 정보 가져와야 하는 상황입니다. 로그인시 pocketHost 먼저 매칭하고, 로그인 성공시에만 local storage 에 저장되게 하고 동명이인 방지를 위해 고유한 ID 값 가져와서 렌더링했습니다.
  • 이때 filter 해서 나온 creator 결과값을 useContext 를 사용해서 값을 읽고 form data 에 append 해서 추가를 하여 console.log 로 출력은 되는 상황이나 pocketHost 에 전송해서 Create 시 form 이 생성되지 않고 있습니다. 콘솔창에 에러메세지가 나타나지 않아 문제의 원인을 찾기 힘든 경우 어떤 부분을 고쳐야 form에 creator 가 찍히도록 데이터를 전송할수있을까요?

Creator.jsx

import { AppContext } from '@/App';
// import FormInput from '../../components/FormInput';
import { useContext, useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { pb } from '@/api/pocketbase';


function Creator() {
  const { updateCreateRoomForm } = useContext(AppContext);
  const [idData, setIdData] = useState([]);
  const [productIdData, setProductIdData] = useState([]);
  const { id } = useParams();


  // localStorage에서 아이디 정보를 가져오는 함수
  const localStorageId = JSON.parse(localStorage.getItem('pocketbase_auth')).model.id;
  // console.log(localStorageId);

  useEffect(() => {
    async function readUserId() {
      try {
        // 포켓 호스트에서 아이디 정보를 가져오는 함수
        const pocketHostId = await pb.collection('users').getFullList();
        // console.log(pocketHostId);
        // localStorageId 과 pocketHostId 두개를 비교하여 일치하는것만 setIdData 에 주입하여 렌더
        pocketHostId.filter((idList) => {
          // console.log(idList.id);
          if (idList.id === localStorageId) {
            setIdData(idList);

          }
        })

      }
      catch (error) {
        if (!(error instanceof ClientResponseError)) {
          console.error(error);
        }
      }

    }


    async function readProductId() {
      try {
        const productId = await pb.collection('products').getFullList({
          expand: 'creator',
          filter: `creator.id="${localStorageId}"`,
        });

        // console.log(productId);
        setProductIdData(productId);

      } catch (error) {
        if (!(error instanceof ClientResponseError)) {
          console.error(error);
        }
      }
    }


    readUserId();
    readProductId()

    updateCreateRoomForm('creator', idData.name);
    console.log(idData.name);
    // console.log(idData);
  }, [idData.name])


  // 이제 이 비교된 결과값을 넣는 Creator input 창이 필요 
  // creator input 의 값을 context api 에서 뽑아서 사용 app context form 에 넣어서 상태 관리
  return (
    <>

      <div readOnly label="생성자" placeholder="생성자" labelClassName="creator" inputClassName="w-full defaultInput">{idData.name}</div>

    </>
  )
}
export default Creator;

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] CreateRoom 페이지 - useContext 사용 이슈

내용

기존에 forwardRef 를 사용하여 form data 페이지와 연결시켰던 코드를 useContext를 사용하여 리팩토링 중입니다. useContext 를 사용하요 select option 에서 선택된 결과값을 useContext(AppContext) 데이터에 넣으려고 시도중입니다. 그런데 selection option 중 선택된 결과값을 setSelectedCategory 에 넣고 콘솔에 로그를 찍어보니 undefined 으로 뜹니다.

useContext 를 사용하여 선택된 결과값을 append 하려면 어느 부분을 고쳐야 할까요?

제가 표현하려는 구조는 아래 그림과 같습니다. 선택된 Category 결과값을 forwardRef 가 아닌 useContext를 사용하여 form 에 입력하려고 합니다.
image

CategoryDropdown.jsx

import { useId, useContext, useState, useEffect } from 'react';
import { category } from '../../data/category';
import { AppContext } from '../../App';

function CategoryDropdown({ className, title }) {
  const { id } = useId();
  const { updateCreateRoomForm } = useContext(AppContext);
  const [selectedCategory, setSelectedCategory] = useState();

  useEffect(() => {

    async function readCategoryList() {
      try {
        const categoryList = category.map((list) => list.title);
        console.log(categoryList);
        console.log(setSelectedCategory);

      } catch (error) {
        console.error(error);
      }
    }

    readCategoryList()

    updateCreateRoomForm('category', selectedCategory);
    console.log(selectedCategory);
  }, []);

  return (
    <div>
      <label htmlFor={id}>{title}</label>
      <select
        value={selectedCategory} onChange={(e) => setSelectedCategory(e.target.value)}
        id={id}
        className={className}
        name="category"
        defaultValue="전체"
      >
        {category.map((list) => (
          <option key={list.title} value={list.title}>
            {list.title}
          </option>
        ))}
      </select>
    </div>
  );
}

export default CategoryDropdown;

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 채팅 페이지 - 채팅 리스트 구현

내용

🛠️ 포켓베이스 리얼 타임을 활용한 일부 채팅 리스트 구현

시안 이미지

게시글_채팅_주문 금액(주문 받기)

달성도

2023년 9월 21일
채팅 기능 구현 방법 문의 완료

2023년 9월 27일
채팅 기능 구현 완료

※ 댓글에 진행사항을 업데이트

[R09M App] CreateRoom 페이지 - form 입력값 이슈

내용

ParticipateCounter.jsx 안에 있는 <p ref={isRef}>{count}</p> 요소의 current.value 값을 const counterValue = counterRef.current.value 로 받아서 pockethost 에 등록시켜주려고 하고 있으나 요소가 <p>결과값</p> 형식으로 들어와버려서 데이터에 등록을 할수 없습니다. 현재 결과값만을 문자로 받아오고 싶은데 current.value 가 아닌 다른 형식으로 결과값을 받아야 할까요?

function CreateRoom() {


  const formRef = useRef(null);

  const counterRef = useRef(null);

  const handleCreate = async (e) => {
    e.preventDefault();

    // 오류 : counter 에 current.value 가 안찍힌다.
    const counterValue = counterRef.current.value;
    console.log(counterValue);


    const data = new FormData();

    data.append('participateNumber', counterValue);
 
return ...
}
import { useState } from "react";
import { minus, plus } from '../../src/assets/icons/svg-icons';

function ParticipateCounter({ title, isRef }) {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
  };

  const decrementCount = () => {
    setCount(count - 1);
  };


  return (
    <div>
      <label>{title}</label>
      <div className="flex gap-2 float-right p-2 items-center">
        <button type="button" onClick={decrementCount}>
          <img src={minus} alt="minus" />
        </button>

        <p ref={isRef}>{count}</p>

        <button type="button" onClick={incrementCount}>
          <img src={plus} alt="plus" />
        </button>
      </div>
    </div>
  );
};

export default ParticipateCounter;

참고 이미지 (선택)

현재 console.log(counterValue); 가 콘솔에 결과값으로 찍힙니다.
원하는 형식 : 결과값 ‘4’ 만을 받아서 pockethost 에 등록할 예정입니다.
image(width="140px")

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 로그인 페이지 - 로그인 구현

내용

🛠️ 어떤 기능을 구현하나요?
이메일, 비밀번호 입력 시 포켓호스트에 등록된 정보와 일치 여부 확인, 로그인 된 정보 로컬스토리지에 저장

시안 이미지

없음

달성도

포켓호스트에 등록된 정보 일치 여부 확인 및 로그인 정보 로컬스토리지에 저장

[R09M App] CreateRoom 페이지 - PocketHost 양식에 Date와 Time을 동시에 입력 이슈

내용

pocketHost 에 시간날짜 form 을 Create 하는 중입니다.
DatePicker 에 대한 양식은 <input type="date"> 로 구현하여 form 에 맞게 전송하는데 성공했으나 포켓호스트 양식에 맞는 시간 형식을 동시에 입력할수 없는 상황입니다.

<input type="datetime-local"> 형식으로 구현할 경우 2018-06-12T19:30 형식으로 입력되어 포켓호스트 양식과 일치하지 않아 form에 올라가지 않는 문제가 있습니다.

DatePicker.jsx
현재 구현한 날짜 구현 코드입니다.

import { forwardRef, useId, useState } from 'react';
// import Input from "@/components/Input";

function DatePicker({ title, className, labelClassName, ...restProps }, ref) {
  const [, setDate] = useState(null);
  const id = useId();

  return (
    <div>
      <label htmlFor={id} className={labelClassName}>
        {title}
      </label>
      <input
        id={id}
        ref={ref}
        className={className}
        type="date"
        onChange={(e) => setDate(e.target.value)}
        {...restProps}
      />
    </div>
  );
}

export default forwardRef(DatePicker);

시간 데이터를 "00:00:00" 형식으로 다음과 같은 방법 구현하여 사용하려해도 한 입력폼에 date 와 time 이 동시에 들어가야하는 문제점이 있습니다.

TimePicker.jsx

import { useState } from 'react';

function TimePicker() {
  const [time, setTime] = useState('00:00:00');

  const handleTimeChange = (e) => {
    const inputTime = e.target.value;
    // 정규식을 사용하여 입력된 값이 유효한 형식인지 확인
    const timeRegex = /^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/;
    if (timeRegex.test(inputTime)) {
      setTime(inputTime);
    }
  };

  return (
    <div>
      <label htmlFor="timeInput">Time:</label>
      <input
        type="text"
        id="timeInput"
        value={time}
        onChange={handleTimeChange}
      />
    </div>
  );
}

export default TimePicker;

참고 이미지 (선택)

문제 상황

Untitled
  • 날짜 데이터는 구현되었으나, 시간 데이터는 포켓호스트양식에 맞게 입력시키지 못하는 상황
  • 날짜 데이터와 시간 데이터를 각각 작업하여 구현하여도 포켓호스트 양식에 맞게 날짜와 시간을 한 form 안에 동시에 입력시키지 못하는 상황

아래는 데이터가 입력되어 생성되는 포켓호스트 폼 형식입니다.

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 프로필 페이지 - 로그아웃 버튼 분리 및 설정 버튼 추가

내용

🛠️ 버튼 분리 및 추가

  • 기존에 회원 정보 아래에 위치한 로그아웃 버튼 위치 및 크기 조정(+ 호버 시 효과 적용)
  • 회원탈퇴 버튼 설정 페이지로 이동 배치
  • 회원 정보 오른쪽 상단에 설정 버튼 추가

시안 이미지

기존

image

변경

image image

달성도

10월 5일

  • 회원탈퇴 버튼 삭제
  • 로그아웃 버튼 스타일링 변경
  • 설정 버튼 추가 및 페이지 연결

[R09M App Backlog] 참여자 목록 페이지 - 참여자 목록 화면에 렌더링

내용

🛠️ 포켓베이스를 이용하여 참여자 목록을 화면에 렌더링

시안 이미지

🌃 작업할 이미지를 첨부해 주세요.
image

달성도

🕒 100% 달성 시까지 얼마나 걸릴지 예상 작업 시간을 작성해 주세요.
9월6일
포켓베이스 데이터 입력

9월7일 참여자 목록 화면에 렌더링

※ 댓글에 진행사항을 업데이트

[R09M App Backlog] 픽업위치 확인 페이지(DetailMap) - 물품을 공구할 픽업 위치 정보를 화면에 렌더링

내용

🛠️
게시물 상세 페이지(Detail 페이지)의 픽업 위치를 클릭 시 지도 API를 이용하여 픽업 위치와 현재 위치를 표시해주는 기능

시안 이미지

🌃 작업할 이미지를 첨부해 주세요.
image

달성도

🕒 100% 달성 시까지 얼마나 걸릴지 예상 작업 시간을 작성해 주세요.
9월 10일 작업 시작
9월 15일 작업 완료 예정
※ 댓글에 진행사항을 업데이트

[R09M App Backlog] 참여 유저 목록 페이지(Users) - 디테일 페이지의 공구에 참여한 참여 유저 목록 렌더링 구현

내용

🛠️ 해당 공구 물품의 참여한 유저의 목록을 보여주는 참여 유저 목록 페이지 화면에 렌더링

  • 포켓 호스트로 products의 데이터를 관계 확장하여 게시물 작성자와 참여자를 불러옵니다.

시안 이미지

🌃 작업할 이미지를 첨부해 주세요.
image

달성도

🕒 100% 달성 시까지 얼마나 걸릴지 예상 작업 시간을 작성해 주세요.
9월 5일 작업시작
9월 8일 작업 완료 예정
※ 댓글에 진행사항을 업데이트

[R09M App] CreateRoom 페이지 - 토글 버튼 제작 이슈

내용

아래와 같은 형태의 토글 버튼을 구현 중입니다.

둘중 하나의 버튼을 눌렀을때 선택된 결과값을 뽑아내서 form data 에 전송해야합니다.

  1. 현재 아래 코드와 같이 삼항연산자를 사용하여 토글이 눌렸을때와 안눌렸을때를 제작 중인데, ui 구현을 좀더 간결하게 작성할수 있는 방법이 있는지 문의드립니다.
  2. 두 선택지중 클릭된 버튼의 결과값인 text value 를 출력하여 form data 에 맞는 양식인 string문자열으로 송출하기 위해 코드를 어떻게 수정해야할지 문의드립니다.
    (현재는 “계좌 이체 만나서 결제” 이렇게 두개 모두 출력되는 상황입니다. “계좌 이체” 혹은 “만나서 결제” 둘중 하나가 출력되도록 하고 싶습니다.)
{isToggled ? 
            (<div className="flex w-full items-center "><div className="w-1/2 shadow-lg bg-white rounded-lg "> 계좌 이체 </div><div className="w-1/2 "> 만나서 결제 </div></div>) :
            (<div className="flex w-full">< div className="w-1/2" > 계좌 이체 </div><div className="w-1/2 shadow-lg bg-white rounded-lg">만나서 결제</div> </div>)}

PaymentToggleButton.jsx

import React, { useState, forwardRef } from "react";

function PaymentToggleButton({ labelClassName, title, }, ref) {
  const [isToggled, setToggled] = useState(false);
  return (
    <>
      <label className={labelClassName}>{title}</label>
      <div className="bg-greenishgray-200 w-full p-2 rounded-lg">

        <button
          ref={ref}
          className="w-full"
          type="button"
          onClick={() => setToggled(!isToggled)}
        >
          {/* 토글이 되면 ? 왼쪽이 활성화된 ui : 오른쪽이 활성화된 ui */}
          {/* 스타일에만 isToggled 를 준다면 ? 콘솔에는 여전히 둘다 찍힐까? */}
          {isToggled ?
            (<div className="flex w-full items-center "><div className="w-1/2 shadow-lg bg-white rounded-lg "> 계좌 이체 </div><div className="w-1/2 "> 만나서 결제 </div></div>) :
            (<div className="flex w-full">< div className="w-1/2" > 계좌 이체 </div><div className="w-1/2 shadow-lg bg-white rounded-lg">만나서 결제</div> </div>)}
          {/* {isToggled ? "계좌 이체" : "만나서 결제"} */}
        </button>

      </div >
    </>
  );
};

export default forwardRef(PaymentToggleButton);

참고 이미지 (선택)

구현하고자 하는 토글 버튼의 이미지입니다.
image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] CreateRoom 페이지 - 사진 업로드 Context 사용시 value 유지와 폼으로 업로드 불가한 이슈

내용

Context API 를 사용해서 파일 업로드 기능을 제작중입니다.

value 값을 기억해서 다른 페이지를 다녀와도 사진이 그대로 유지되어있도록 할 예정입니다.

현재 useState 를 이용하여 fileImages 데이터 안에 value를 넣어주었으나 value 값을 기억하지 않고 form 에 전송이 되지 않는 상황입니다. 업로드한 이미지를 useContext 로 CreateRoom 페이지로 데이터를 전달시켜서 Create 해야하는 상황인데 form 에 입력되지 않는 이유와 수정되야할 부분을 알수있을까요?

import { AppContext } from '@/App';
import imgUpload from '@/assets/icons/imgUpload.svg';
import { useContext, useEffect, useState } from 'react';

function FileUpload({ value, className, title, ...restProps }) {
  const { updateCreateRoomForm } = useContext(AppContext);
  const [fileImages, setFileImages] = useState(value);

  useEffect(() => {
    updateCreateRoomForm('uploadImage', fileImages);
  }, [fileImages]);

  const handleFileUpload = (e) => {
    const { files } = e.target;
    const fileImages = Array.from(files).map((file) => ({
      image: URL.createObjectURL(file),
      label: file.name,
    }));
    setFileImages(fileImages);
  };

  return (
    <>
      <div className=" mb-14">
        <div className="flex justify-between items-center">
          <p>사진 업로드</p>
          <label htmlFor="photo" className={className}>
            {title}
          </label>
          <input
            type="file"
            accept="image/jpg,image/png,image/jpeg,image/webp,image/avif"
            name="photo"
            id="photo"
            onChange={handleFileUpload}
            style={{ display: 'none' }}
            {...restProps}
          />
        </div>
        <div className="flex justify-center border-2 my-4 rounded-lg border-dashed border-line-400">
          {!fileImages && (
            <img src={imgUpload} alt="photo" className="w-20 p-4" />
          )}
          {fileImages?.map((file) => {
            return <img value={fileImages}
              key={file.label} src={file.image} alt={file.label} />;
          })}
        </div>
      </div>
    </>
  );
}

export default FileUpload;
import { AppContext } from '@/App';
import { useContext, useEffect, useState } from 'react';


function ContentTextarea(
  { value = "", title, placeholder, className, labelClassName, ...restProps }) {
  const { updateCreateRoomForm } = useContext(AppContext);
  const [data, setData] = useState(value);

  useEffect(() => {
    updateCreateRoomForm('content', data);
  }, [data]);

  const handleInputChange = (e) => {
    setData(e.target.value);
  };

  return (
    <>
      <label htmlFor="content" className={labelClassName}>
        내용
      </label>
      <textarea
        value={data}
        onChange={handleInputChange}
        id="content"
        placeholder={placeholder}
        className={className}
        {...restProps}
      />
    </>
  );
}

export default ContentTextarea;

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] CreateRoom 페이지 - 파일업로드 이슈

내용

파일 업로드 기능을 FileUpload.jsx 파일에 구현하여 아래와 같이 컴포넌트로 제작하였습니다.
파일업로드 컴포넌트로 업로드한 파일을 pocketHost 에 있는 form data 로 전송을 해야하는 상황입니다.
해당 양식에 맞게 파일을 첨부하여 pocketHost 에 전송에 성공하였으나
파일 미업로드시에는 아래와 같이 에러메세지가 뜹니다.
파일 미업로드시 에러메세지를 예방하는 법을 문의합니다.

import { useState, forwardRef } from "react";
import { imgUpload } from '../../assets/icons/svg-icons'

function FileUpload({ }, ref) {

  const [fileImages, setFileImages] = useState(null);

  const handleFileUpload = (e) => {
    const { files } = e.target;
    const fileImages = Array.from(files).map((file) => ({
      image: URL.createObjectURL(file),
      label: file.name,
    }));
    setFileImages(fileImages);

    // img file const 
    console.log(photoRef.current.files);
    const photoValue = photoRef.current.files;

    if (!photoValue) {
      // toast message here
      return
    }
  };

  return (<>
    <div >
      <label htmlFor="photo" className="sr-only">
        사진 등록
      </label>
      <input
        type="file"
        accept="image/jpg,image/png,image/jpeg,image/webp,image/avif"
        name="photo"
        id="photo"
        ref={ref}
        onChange={handleFileUpload}
        className=""
      />
      <div className="" >
        {!fileImages && (
          <img
            src={imgUpload}
            alt="photo"
            className="w-24"
          />
        )}
        {fileImages?.map((file) => {
          return (
            <img
              key={file.label}
              src={file.image}
              alt={file.label}
              className=""
            />
          );
        })}
      </div>
    </div>
  </>)
}
export default forwardRef(FileUpload);

아래 코드를 적용시켜보았지만 똑같은 에러메세지가 나는 상황입니다. 파일 미업로드시 뜨는 에러를 어떻게 예방해야할까요?

 } catch (error) {
       if (!(error instanceof ClientResponseError)) {
         console.error(error);
       }}

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 설정 페이지 - 비밀번호 변경 기능 구현

내용

🛠️ 비밀번호 변경 버튼 클릭 시 이동하는 페이지에서 기존 비밀번호, 새 비밀번호 입력 후 변경하는 로직(기능) 구현

시안 이미지

image

달성도

10월 11일

  • 비밀번호 변경 버튼 클릭 시 페이지 이동
  • 입력한 기존 비밀번호와 로컬 스토리지에 저장된 유저 비밀번호 대조
  • 변경할 비밀번호 입력 후 변경 버튼 누를 시 pocketHost에 등록된 유저 정보 업데이트

[R09M App] 메인 페이지 - 지도 흔들림 현상 및 TanStack Query 적용

내용

🤔 문제 상황

  • pocketHost products 컬렉션에 있는 meetingPoint 카테고리의 주소 정보를 메인페이지(http://localhost:3000/home)에 마크하는 기능을 구현하였습니다. 그러나 렌더링 될 때마다 아래 영상과 같이 위치의 이동에 따라 지도가 흔들려보이는 현상이 발생되고 있습니다.
  • 원인은 데이터가 렌더링 되면서 meetingPoint마다 위도, 경도가 이동하여 다음과 같이 지도가 흔들려보이는 것이라고 생각합니다.
  • 렌더링 시 매번 지도가 흔들려보이는 것은 사용자의 입장에서 좋지 않은 경험이라고 생각하여, 이를 개선하고 싶습니다.
  • 이와 더불어 현재 지도의 경우 js 파일로 분리되어 있어 TanStack Query를 사용할 수 없었습니다. 그러나 매번 렌더링 시 지도의 호출을 기다리는 것은 사용자의 입장에서 좋지 않은 경험이라고 생각하여 지도를 매번 렌더링하지 않고 캐싱(?)하는 방법이 있는지 궁금합니다.

참고 이미지

지도 흔들림 현상

[R09M App Backlog] 설정 페이지 - 회원탈퇴 로직 구현

내용

🛠️ 회원탈퇴 버튼 클릭 시 등록된 비밀번호 확인 후 탈퇴 진행 로직 구현

시안 이미지

image image

달성도

10월 15일

  • 회원탈퇴 버튼 클릭 시 모달 다이얼로그로 비밀번호 확인 진행
  • 등록된 유저 비밀번호와 입력한 비밀번호 일치 여부 확인
  • 비밀번호가 일치할 시 탈퇴 진행

[R09M App Backlog] 메인 페이지 - pocketHost를 활용한 픽업 위치 표시

내용

🛠️ pocketHost products 데이터를 활용하여 픽업 위치를 지도에 표시하는 기능 구현

  • 기존에 위도와 경도값을 직접 계산하고 구현한 것을 카카오 지도 API와 pocketHost에 등록한 주소를 바탕으로 서버와 연결하여 표시하도록 함

시안 이미지

image

달성도

9월 11일

  • 클라이언트 SDK을 활용하여 fields 데이터 가져오기
  • 가져온 데이터를 바탕으로 지도에 표시

9월 12일

  • 오류사항에 대한 원인 파악 후 작업 마무리

[R09M App Backlog] 상세 페이지 - 공동구매 진행상태 업데이트 기능 구현

내용

🛠️ pocketHost를 활용한 공동구매 진행상태 렌더링 및 업데이트 기능

  • pocketHost를 활용하여 status 데이터 가져오기
  • 데이터에 따라 상태 표시 이미지 변경
  • 드롭다운 기능을 활용하여 대기중, 진행중, 공구완료 선택 시 pocketHost에 status 데이터 정보 변경(업데이트)

시안 이미지

image

달성도

9월 14일

  • 클라이언트 SDK getOne을 활용하여 데이터 정보(status) 가져오기
  • 데이터에 따라 상태 표시 이미지 변경
  • slect, option(HTML 마크업)을 활용하여 대기중, 진행중, 공구완료 드롭다운 생성
  • pocketHost 클라이언트 SDK PATCH(update)를 활용하여 status 데이터 수정

9월 15일

  • 공동구매 진행상태 페이지에 상품 정보 추가

[R09M App] CreateRoom 페이지 - forwardRef 이슈

내용

Status.jsx 컴포넌트를 forwardRef 를 사용하고 CreateRoom.jsx 페이지로 불러와서 ref={statusRef} 를 적용시켜 form data 로 입력하는 과정에서 오류가 발생하고 있습니다.
selection > option 에 담긴 대기중/진행중/공구종료 상태들을 컴포넌트화하여 forwardRef 를 주어 참조시키고 form 데이터로 입력하는 과정에서의 form으로 불러들이지 못하는 에러를 어떻게 해결하면 될까요 ?

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 프로필 페이지 - 로그인한 사용자 정보 가져오기

내용

🛠️ pocketHost와 로컬스토리지 데이터 대조

  • 로그인한 사용자(로컬 스토리지에 등록)와 pocketHost에 등록된 유저 정보 대조
  • 일치하는 유저의 정보 가져오기

시안 이미지

없음

달성도

9월 16일

  • 로컬스토리지에 등록된 데이터 가져오기
  • pocketHost에 있는 유저 정보 중 일치하는 정보 가져오기
  • 프로필 사진, 이름, 가입일 데이터 가져오기
  • 유저 정보와 상품 정보를 활용하여 유저별 판매 상품 렌더링

[R09M App] CreateRoom 페이지 - useRef 이슈

내용

component 들을 작업중인 페이지에 불러와서 useRef 를 주었을때 다음과 같이 forwardRef() 에러가 나타납니다.
컴포넌트를 불러와서 useRef 를 주는 경우 한번 ref 를 선언하고 컴포넌트 내에서 연결시켜줘야할까요?
아니면 forwardRef 를 사용해야할까요 ? useRef 를 한번 선언해 연결시켜주는 방법이 있을까요 ?

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 게시물 상세 페이지(Detail) - 모달 다이얼로그 기능 구현

내용

🛠️ 참여하기, 취소하기, 더 보기 버튼 클릭 후 등장하는 모달 다이얼로그 기능 구현

시안 이미지

🌃 작업할 이미지를 첨부해 주세요.
image
image

달성도

🕒 100% 달성 시까지 얼마나 걸릴지 예상 작업 시간을 작성해 주세요.
9월 14일 작업 시작
9월 20일 작업 완료 예정
※ 댓글에 진행사항을 업데이트

[R09M App] CreateRoom 페이지 - 상태값 저장 이슈

내용

useContext 로 상태관리를 하면서 form 이 모두 담긴 페이지에서 한 카테고리 페이지를 클릭 후 돌아올때 다른 form 의 요소들이 그대로 저장되어 있도록 구현하려면 tan stack query를 사용해야할까요 ? 현재 Location 페이지에 다녀오면 입력했던 form의 선택값들이 reset 되는 상황입니다.

ContentTextarea.jsx

import { AppContext } from '@/App';
import { useEffect, useState, useContext, useId } from 'react';


function ContentTextarea(
  { title, placeholder, className, labelClassName, ...restProps }) {
  const { id } = useId();
  const { updateCreateRoomForm } = useContext(AppContext);
  const [data, setData] = useState('');

  useEffect(() => {
    updateCreateRoomForm('content', data);


  }, [data])

  const handleInputChange = (e) => {

    setData(e.target.value);

    // console.log(setData);

  };

  return (
    <>
      <label htmlFor={id} className={labelClassName}>
        {title}
      </label>
      <textarea
        value={data}
        onChange={handleInputChange}
        id={id}
        placeholder={placeholder}
        className={className}
        {...restProps}
      />
    </>
  );
}

export default ContentTextarea;

CreateRoom.jsx

import { AppContext } from '@/App';
import { pb } from '@/api/pocketbase';
import Button from '@/components/Button';
import FormInput from '@/components/FormInput';
import CreateHeader from '@/layout/CreateHeader';
import CategoryDropdown from '@/parts/create/CategoryDropdown';
import ContentTextarea from '@/parts/create/ContentTextarea';
import DatePicker from '@/parts/create/DatePicker';
import FileUpload from '@/parts/create/FileUpload';
import MeetingPoint from '@/parts/create/MeetingPoint';
import ParticipateCounter from '@/parts/create/ParticipateCounter';
import PaymentToggleButton from '@/parts/create/PaymentToggleButton';
import Status from '@/parts/create/Status';
import { ClientResponseError } from 'pocketbase';
import { useContext, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import Creator from '@/parts/create/Creator';
import { useEffect } from 'react';



function CreateRoom() {
  const { createRoomForm, updateCreateRoomForm } = useContext(AppContext);

  const formRef = useRef(null);
  const titleRef = useRef(null);
  const priceRef = useRef(null);
  const dateRef = useRef(null);
  const paymentRef = useRef(null);
  const ParticipateCounterRef = useRef(null);
  const uploadImageRef = useRef(null);
  const statusRef = useRef(null);

  const handleCreate = async (e) => {
    e.preventDefault();

    const categoryValue = createRoomForm.category;
    const titleValue = titleRef.current.value;
    const contentValue = createRoomForm.content;
    console.log(contentValue);
    const priceValue = priceRef.current.value;
    const dateValue = dateRef.current.value;

    const paymentValue = paymentRef.current.dataset.payment;
    const ParticipateCounterValue = Number(
      ParticipateCounterRef.current.textContent
    );

    const meetingPointValue = createRoomForm.meetingPoint;
    const creatorValue = createRoomForm.creator.id;

    const uploadImageValue = uploadImageRef.current.files[0];
    const statusValue = statusRef.current.value;

    const data = new FormData();

    data.append('category', categoryValue);
    data.append('title', titleValue);
    data.append('content', contentValue);
    data.append('price', priceValue);
    data.append('pickup', dateValue);
    data.append('payment', paymentValue);
    data.append('participateNumber', ParticipateCounterValue);
    data.append('meetingPoint', meetingPointValue);
    data.append('creator', creatorValue);
    data.append('participate', creatorValue);
    if (uploadImageValue) {
      data.append('uploadImage', uploadImageValue);
    }
    data.append("status", statusValue);

    for (const [key, value] of data.entries()) {
      console.log(key, value);
    }

    // return
    try {
      await pb.collection('products').create(data);

      // navigate('/products');

    } catch (error) {
      if (!(error instanceof ClientResponseError)) {
        console.error(error);
      }
    }
  }

  return (
    <>

      <Helmet>
        <title>방만들기</title>
      </Helmet>

      <div >
        <CreateHeader />

        <form
          encType="multipart/form-data"
          ref={formRef}
          onSubmit={handleCreate}
        >
          <div className="flex flex-col gap-4 p-4 relative"
          >

            <CategoryDropdown
              title="카테고리"
              className="w-full defaultInput"
            />
            <FormInput
              ref={titleRef}
              type="text"
              placeholder="상품명을 입력해주세요."
              labelClassName="product name"
              inputClassName="defaultInput w-full"
              label="상품명"
            />
            <FormInput
              ref={priceRef}
              type="number"
              placeholder="0원"
              labelClassName="product price"
              inputClassName="defaultInput w-full"
              label="상품 가격"
            />
            <ContentTextarea
              title="내용"
              placeholder="공구 모임 주요내용을 알려주세요."
              className="w-full defaultInput"
              labelClassName="product content"
            />

            <DatePicker
              ref={dateRef}
              title="픽업 날짜"
              className="w-full defaultInput"
              labelClassName="date Picker"
            />

            <Status
              ref={statusRef}
              title="상태"
              className="w-full defaultInput "
              labelClassName="status"
            />

            <Creator />

            <PaymentToggleButton
              ref={paymentRef}
              title="정산 방법"
              labelClassName="payment"
            />

            <ParticipateCounter ref={ParticipateCounterRef} title="인원" />

            <MeetingPoint title="만날 장소" />

            <FileUpload
              ref={uploadImageRef}
              title="파일 업로드"
              className="bg-[#EBF8E8] p-4 rounded-lg text-primary-500"
            />
          </div>
          <div className="bg-white fixed bottom-0 max-w-xl w-full p-4 drop-shadow-2xl">
            <Button type="submit" className="activeButton lgFontButton w-full ">
              방 만들기
            </Button>
          </div>
        </form>
      </div>
    </>
  );
}

export default CreateRoom;

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App] 알림 - 기능 구현 방법 문의 이슈

내용

저희가 만든 “공구룸” 웹앱 서비스에서 알림 기능을 구현하려고 합니다.

포켓베이스 리얼타임 기능을 활용하면 푸시 알림 기능을 구현할수 있을것 같은데
푸시알림에 대한 자료를 검색하면 firebase 관련 자료밖에 나오지 않아서 알림 기능 구현 방법에 대해 문의합니다.

  • 적용할 알림 기능 정리
  1. 관심상품 체크를 하면 관심상품이 새롭게 등록되었을 때 알림을 해주는 기능
  2. 내가 공동구매에 참여하겠다고 한 상품의 진행상태가 변경되었을 때 알림을 해주는 기능
  3. 새로운 알림이 있을때 빨간 점으로 아이콘에 표시 / 읽은 뒤 빨간점은 사라짐
  4. (채팅 기능 구현 시, 채팅이 왔을때 알림 기능 구현 예정)
  • 관심 상품 등록
  1. 원하는 상품의 키워드를 태그 형식으로 등록
  2. 해당 태그 글자가 포함된 상품의 이름이 새롭게 등록되면 알림을 날릴 예정
    filter : ~ 상품이름 포함

찾아본 푸시 알림 자료

  • 카카오 api 를 사용한 푸시 알림 기능

[Kakao Developers](https://developers.kakao.com/docs/latest/ko/push/rest-api)

  • Push Notification Custom Hook 만들어서 알림 구현

[React에서 Browser Notification (푸시 알림) 구현하기](https://mingule.tistory.com/68)

  • Notification API 의 공식 문서

[Notifications API Standard](https://notifications.spec.whatwg.org/)

  • chat GPT 에게 물어본 결과 코드
import React, { useState } from 'react';

function NotificationSender() {
  const [message, setMessage] = useState('');

  const sendNotification = async () => {
    try {
      const response = await fetch('YOUR_POCKETBASE_API_URL/send-notification', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer YOUR_AUTH_TOKEN',
        },
        body: JSON.stringify({ message }),
      });

      if (response.ok) {
        console.log('Notification sent successfully');
        // 여기에서 필요한 처리를 수행하세요.
      } else {
        console.error('Error sending notification:', response.statusText);
      }
    } catch (error) {
      console.error('Error sending notification:', error);
    }
  };

  return (
    <div>
      <h2>Send Notification</h2>
      <textarea
        rows="3"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button onClick={sendNotification}>Send</button>
    </div>
  );
}

export default NotificationSender;

참고 이미지 (선택)

시안 이미지

MainDelivery

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

[R09M App Backlog] 로그인 / 회원가입 페이지 - 정규표현식 적용

내용

🛠️ 아이디는 이메일 형식, 비밀번호는 최소 8자 이상 입력하지 않은 시 안내 문구 적용

시안 이미지

없음

달성도

  • 아이디 입력 시 이메일 형식이 아닐 때 입력창 하단에 안내 문구 표시
  • 비밀번호 입력 시 8자 미만일 때 입력창 하단에 안내 문구 표시

[R09M App] CreateRoom 페이지 - ref 이슈

내용

Warning: Unexpected ref object provided for select. Use either a ref-setter function or React.createRef().

console 에 계속해서 다음과 같은 warning 메세지가 나타납니다. 문제가 나타나는 Status.jsx 파일안에서는 forwardRef를 사용중입니다. ref 코드에는 이상이 없어보입니다. 사용에는 이상이 없지만 해당 메세지가 안뜨게 하려면 어떻게 해야할까요?

import { string } from 'prop-types';
import { useId, forwardRef } from 'react';

function Status({ title, className, labelClassName, ...restProps }, ref) {
  const { id } = useId();

  return (
    <>
      <label htmlFor={id} className={labelClassName}>
        {title}
      </label>
      <select
        ref={ref}
        id={id}
        className={className}
        {...restProps}
        name="status"
        defaultValue="대기중"
      >
        <option value="대기중">대기중</option>
        <option value="진행중">진행중</option>
        <option value="공구종료">공구종료</option>
      </select>
    </>
  );
}

export default forwardRef(Status);

참고 이미지 (선택)

image

※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.