카테고리 없음

Fiesta Tour_8일차

juni_shin 2024. 7. 25. 23:03

검색해서 알아보기

  • 제네릭 빈 배열
    • types/supabase.ts에 public 스키마에 모든 테이블 , 컬럼 불러오기
      • npx supabase login -> npx supabase init -> n / n -> yarn add supabase --dev 
const [reviews, setReviews] = useState<Tables<'reviews'>[]>([]);

 

router.push() ( next/navigation )

router.push()는 Next.js의 useRouter 훅을 사용하여 프로그래밍 방식으로 내비게이션을 수행할 때 사용합니다.

장점

  • 프로그램적 내비게이션: 내비게이션을 함수 내에서 조건적으로 실행할 수 있습니다. 예를 들어, 폼 제출 후 특정 조건에 따라 페이지를 이동시키는 경우에 유용합니다.
  • 동적 경로: 특정 이벤트나 사용자 상호작용 후에 경로를 변경할 수 있습니다.

단점

  • 명시적인 코드 필요: 각 내비게이션마다 함수를 작성해야 하므로 코드가 다소 복잡해질 수 있습니다.
  • 유지보수: 내비게이션 로직이 여러 컴포넌트에 분산될 수 있어 유지보수가 어려워질 수 있습니다.

Link 태그

Link 태그는 Next.js의 next/link 컴포넌트를 사용하여 내비게이션 링크를 생성합니다.

장점

  • 간결한 문법: 내비게이션을 위한 선언적 문법을 제공하여 코드가 간결해집니다.
  • SEO 최적화: Link 컴포넌트는 Next.js의 최적화 기능과 함께 사용되어 SEO 친화적인 내비게이션을 제공합니다.
  • 자동 프리페칭: 기본적으로 Next.js는 Link 컴포넌트를 사용할 때 해당 페이지를 미리 가져오는 기능을 제공합니다.

단점

  • 유연성 부족: 조건부 내비게이션이나 복잡한 내비게이션 로직을 처리하기에는 적합하지 않습니다.
  • 기본 동작 제한: Link 컴포넌트는 특정 동작(예: 모달 창 닫기 등)을 실행하면서 내비게이션을 수행해야 하는 경우에는 불편할 수 있습니다.

결론

  • router.push(): 동적, 조건부, 프로그래밍 방식 내비게이션에 유리합니다. 특정 이벤트 후에 페이지를 이동시키는 경우에 적합합니다.
  • Link 태그: 정적, 선언적, 간단한 내비게이션에 유리합니다. 페이지 내에서 간단한 내비게이션 링크를 생성하는 경우에 적합합니다.

 

    • 서버 측 (password/route.ts)
      • PUT 메소드 핸들러는 요청에서 현재 비밀번호와 새로운 비밀번호를 가져옵니다.
      • supabase.auth.signInWithPassword 메소드를 사용하여 현재 비밀번호의 유효성을 확인합니다. 이 메소드는 제공된 이메일과 비밀번호로 사용자를 인증합니다.
      • 인증에 성공하면, supabase.auth.updateUser 메소드를 사용하여 새로운 비밀번호로 업데이트합니다.
      • 인증에 실패하면, 오류 메시지를 반환합니다.
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@/utils/supabase/server';

export async function PUT(req: NextRequest, { params }: { params: { id: string } }) {
  const supabase = createClient();
  const { id: user_id } = params;
  const { currentPassword, newPassword } = await req.json();

  try {
    // 현재 비밀번호 확인
    const {
      data: { session },
      error: signInError
    } = await supabase.auth.signInWithPassword({
      email: req.headers.get('email') || '', // 헤더에서 이메일 가져오기, 프론트엔드에서 제공
      password: currentPassword
    });

    if (signInError || !session) {
      // 현재 비밀번호가 일치하지 않으면 오류 반환
      return NextResponse.json({ error: 'Current password is incorrect' }, { status: 401 });
    }

    // 비밀번호 변경
    const { error: updatePasswordError } = await supabase.auth.updateUser({
      password: newPassword
    });

    if (updatePasswordError) {
      return NextResponse.json({ error: updatePasswordError.message }, { status: 500 });
    }

    return NextResponse.json({ message: 'Password updated successfully' }, { status: 200 });
  } catch (error) {
    return NextResponse.json({ error: 'Unexpected error occurred' }, { status: 500 });
  }
}
    • 클라이언트 측 (PasswordChangeForm.tsx)
      • 사용자는 현재 비밀번호와 새로운 비밀번호를 입력합니다.
      • 폼이 제출되면, Axios를 사용하여 서버에 PUT 요청을 보냅니다. 이 요청은 현재 비밀번호와 새로운 비밀번호를 포함하며, 헤더에 사용자의 이메일을 포함합니다.
      • 서버에서 비밀번호 변경이 성공적으로 이루어지면, 사용자에게 알림을 표시합니다. 오류가 발생하면, 오류 메시지를 표시합니다.
'use client';

import axios from 'axios';
import { useState } from 'react';
import { API_MYPAGE_PROFILE_PASSWORD } from '@/utils/apiConstants';

type PasswordChangeFormProps = {
  userId: string;
  email: string;
};

const PasswordChangeForm = ({ userId, email }: PasswordChangeFormProps) => {
  const [currentPassword, setCurrentPassword] = useState('');
  const [newPassword, setNewPassword] = useState('');

  const handlePasswordChange = async (event: React.FormEvent) => {
    event.preventDefault();
    try {
      await axios.put(
        API_MYPAGE_PROFILE_PASSWORD(userId),
        { currentPassword, newPassword },
        {
          headers: {
            email: email
          }
        }
      );
      alert('Password changed successfully');
    } catch (error) {
      alert('Error changing password');
    }
  };

  return (
    <form onSubmit={handlePasswordChange} className="mt-4">
      <div>
        <input
          className="w-full rounded border px-3 py-2 text-black"
          type="password"
          value={currentPassword}
          onChange={(e) => setCurrentPassword(e.target.value)}
          placeholder="Current Password"
        />
      </div>
      <div className="mt-4">
        <input
          className="w-full rounded border px-3 py-2 text-black"
          type="password"
          value={newPassword}
          onChange={(e) => setNewPassword(e.target.value)}
          placeholder="New Password"
        />
      </div>
      <button type="submit" className="mt-4 rounded bg-black px-4 py-2 text-white">
        Change Password
      </button>
    </form>
  );
};

export default PasswordChangeForm;
    • 현재 비밀번호 불일치 및 변경 비밀번호가 규칙을 벗어날 경우

  • 현재 비밀번호 및 변경 비밀번호 규칙이 모두 문제 없을 경우

 

 

내 위치 불러오기 ( 네이버 지도 API 사용 )

  • PC 환경에서 내 위치가 정확히 나오지 않는 경우가 존재
    1. Wi-Fi 기반 위치 추적: PC에서는 GPS가 없기 때문에 Wi-Fi를 기반으로 위치를 추적합니다. Wi-Fi 신호의 세기와 위치 정보의 정확도에 따라 오차가 발생할 수 있습니다.
    2. IP 주소 기반 위치 추적: 일부 경우에는 Wi-Fi 대신 IP 주소를 사용하여 위치를 추적할 수도 있습니다. 이 방법은 정확도가 낮습니다.
    3. 브라우저 권한 설정: 브라우저의 위치 정보 권한 설정에 따라 정확도가 달라질 수 있습니다. 위치 정보 권한이 정확하게 설정되어 있는지 확인해야 합니다.
  • 지오코딩 (Geocoding)예시:
    • 입력: "서울특별시 중구 세종대로 110"
    • 출력: 위도 37.5665, 경도 126.9780
    역지오코딩 (Reverse Geocoding)예시:
    • 입력: 위도 37.5665, 경도 126.9780
    • 출력: "서울특별시 중구 세종대로 110"
  • 역지오코딩은 지오코딩의 반대 과정으로, 위도와 경도 좌표를 주소(또는 장소명)로 변환하는 과정입니다. 예를 들어, 위도 37.5665와 경도 126.9780을 입력하면 해당 좌표의 주소를 반환하는 작업입니다.
  • 지오코딩은 주소(또는 장소명)를 위도(latitude)와 경도(longitude)와 같은 좌표로 변환하는 과정입니다. 예를 들어, "서울특별시 중구 세종대로 110"이라는 주소를 입력하면 해당 주소의 위도와 경도를 반환하는 작업입니다.
  • naver 지도에서 제공해주는 지오코딩 및 역지오코딩을 활용하여 구현
     
'use client';
import { useEffect, useState } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import { toast } from 'react-toastify';

const RegionForm = () => {
  const [isScriptLoaded, setIsScriptLoaded] = useState(false);
  const [position, setPosition] = useState<{ latitude: number; longitude: number } | null>(null);
  const [region, setRegion] = useState<string | null>(null);
  const router = useRouter();
  const pathname = usePathname();

  // URL 경로에서 userId 추출
  const userId = pathname.split('/')[1];

  const loadNaverMapScript = () => {
    const script = document.createElement('script');
    script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${process.env.NEXT_PUBLIC_NCP_CLIENT_ID}&submodules=geocoder`;
    script.async = true;

    script.onload = () => {
      if (window.naver && window.naver.maps) {
        setIsScriptLoaded(true);
      } else {
        toast.error('네이버 맵 스크립트 로드 실패');
      }
    };

    script.onerror = () => {
      toast.error('네이버 맵 스크립트 로드 실패');
    };

    document.body.appendChild(script);
  };

  const getCurrentPosition = () => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (pos) => {
          const { latitude, longitude } = pos.coords;
          setPosition({ latitude, longitude });
        },
        (error) => {
          toast.error(`Error occurred while retrieving location: ${error.message}`);
        }
      );
    } else {
      toast.error('Geolocation is not supported by this browser.');
    }
  };

  const getRegionName = (latitude: number, longitude: number) => {
    if (!window.naver) return;

    const coord = new window.naver.maps.LatLng(latitude, longitude);

    window.naver.maps.Service.reverseGeocode(
      {
        location: coord,
        coordType: window.naver.maps.Service.CoordType.LatLng
      },
      (status: any, response: any) => {
        if (status === 200) {
          if (response.result.items && response.result.items.length > 0) {
            const address = response.result.items[0].address;
            setRegion(address);
          } else {
            toast.error('주소를 찾을 수 없습니다.');
          }
        } else {
          toast.error('Failed to get the location name.');
        }
      }
    );
  };

  const initializeMap = () => {
    if (position) {
      const map = new window.naver.maps.Map('map', {
        center: new window.naver.maps.LatLng(position.latitude, position.longitude),
        zoom: 15
      });

      new window.naver.maps.Marker({
        position: new window.naver.maps.LatLng(position.latitude, position.longitude),
        map: map
      });

      getRegionName(position.latitude, position.longitude); // Rename getLocationName to getRegionName
    }
  };

  const handleSave = () => {
    if (region && userId) {
      router.push(`/${userId}/profilepage`);
    }
  };
  const handleBack = () => {
    router.push(`/${userId}/profilepage`);
  };

  useEffect(() => {
    loadNaverMapScript();
  }, []);

  useEffect(() => {
    if (isScriptLoaded) {
      getCurrentPosition();
    }
  }, [isScriptLoaded]);

  useEffect(() => {
    if (position) {
      initializeMap();
    }
  }, [position]);

  return (
    <>
      <button onClick={handleBack}>Go Back</button>
      <div className="mt-4" id="map" style={{ width: '100%', height: '400px' }}></div>
      {region && <p>현재 위치: {region}</p>}
      <button className="my-4 rounded bg-black p-2 text-white" onClick={handleSave}>
        저장하기
      </button>
    </>
  );
};

export default RegionForm;