반응형

기본적으로 부트스트랩 프레임워크를 기본으로 사용하면서 조금씩 필요한 부분을 css 조정을 하여서 제작하였습니다.


프로젝트 구조

- project_name // 소스 코드 디렉토리

    - index.html // 리액트 인덱스 html

    - img // 정적 이미지 디렉토리

        - logo.png // 파비콘용 로고

    - src // 소스 코드 디렉토리 

        - components // 페이지 영역 디렉토리

            - Header.js // 헤더 영역 폼

            - LiveStream.js // 생방송 표시 폼

            - Login.js // 로그인 폼

            - MainContents.js // 메인 컨텐츠 폼

            - SearchResults.js // 검색 결과 폼

            - SideBar.js // 사이드바 폼

        - img // 이미지 디렉토리

            - alarm.png // 알람에 사용하고자 하는 이미지

            - logo.png // 임시로 사용하는 이미지

        - style

            - App.css

            - Header.css

            - index.css

            - LiveStream.css

            - MainContents.css

            - SearchResults.css

            - SideBar.css

        - App.js

        - index.js



index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <title>PIViewer</title>
    <link rel="icon" href="img/logo.png" type="image/png">
  </head>
  <body>
    <div id="piview" class="h-100"></div>
  </body>
</html>

 

index.js

const root = ReactDOM.createRoot(document.getElementById('piview'));
root.render(
    <React.StrictMode>
        <App/>
    </React.StrictMode>
);

App.js

function App() {
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    const [streramTwitchId, setStreramTwitchId] = useState("");

    const handleLogin = () => {
        setIsLoggedIn(true);
    };

    const handleLogout = () => {
        setIsLoggedIn(false);
    };

    useEffect(() => {
        // Spring 서버로 JSON 데이터를 보내는 함수
        async function sendDataToSpringServer() {
            try {
                const response = await fetch('http://localhost:8080/checkLogin', {
                    method: 'GET',
                    headers: {
                        'Content-Type': 'application/json', // JSON 데이터라는 것을 명시
                    },
                    credentials: 'include',
                });
            
                if (response.ok) {
                    const responseData = await response.json(); // Spring 서버에서의 응답 데이터를 JSON으로 파싱
                    if(responseData === true){
                      // 로그인 확인 함수 실행
                      handleLogin();
                    }
                    console.log('Spring 서버 응답 데이터:', responseData);
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
                console.error('오류 발생:', error);
            }
        }
        sendDataToSpringServer();
    }, []);

    return (
        <Router>
            <div className="h-100">
                <header>
                    <Header isLoggedIn={isLoggedIn} onLogout={handleLogout} />
                </header>
                <div className="d-flex h-100">
                    <div className="d-flex flex-column h-100 bg-body-tertiary sidebar mt-header">
                        <div className="border-bottom add-follow-button">
                            {isLoggedIn && 
                            <Link className="list-group-item list-group-item-action py-3 lh-sm text-center" to='/search'>
                                <strong className="mb-1">팔로우 추가</strong>
                            </Link>
                            }
                        </div>
                        <div className="text-center p-3 border-bottom fs-5 fw-semibold">팔로우 목록</div>
                        <div className="list-group list-group-flush border-bottom scrollarea">
                            {isLoggedIn  ? (
                                <SideBar setStreramTwitchId={setStreramTwitchId}/>
                            ) : (
                                <div className='text-center'>로그인을 해주세요</div>
                            )}
                        </div>
                    </div>
                    <div className='w-100 mt-header contents'>
                        <Routes>
                            <Route path="/search" element={<SearchResults />} />
                            <Route path="/" element={<MainContents isLoggedIn={isLoggedIn} onLogin={handleLogin}/>} />
                            <Route path="/:customUrl" element={<LiveStream streramTwitchId={streramTwitchId} setStreramTwitchId={setStreramTwitchId}/>} />
                            <Route path="/login" element={<Login isLoggedIn={isLoggedIn} onLogin={handleLogin}/>} />
                        </Routes>
                    </div>
                </div>
            </div>
        </Router>
    );
}

export default App;

Header.js

function Header({ isLoggedIn, onLogout }) {
    const navigate  = useNavigate();

    const Logout = (event) => {
      event.preventDefault();
      async function sendDataToSpringServer() {
          try {
              const response = await fetch('http://localhost:8080/logout', {
                  method: 'GET',
                  headers: {
                      'Content-Type': 'application/json', // JSON 데이터라는 것을 명시
                  },
                  credentials: 'include'
              });
          
              if (response.ok) {
                  const responseData = await response.json(); // Spring 서버에서의 응답 데이터를 JSON으로 파싱
                  onLogout();
                  navigate(`/`);
                  console.log('Spring 서버 응답 데이터:', responseData);
              } else {
                  console.error('Spring 서버 응답 에러:', response.status, response.statusText);
              }
          } catch (error) {
              console.error('오류 발생:', error);
          }
      }
      sendDataToSpringServer();
    }

    return ( isLoggedIn ? (
        <Navbar className="px-3 py-0 bg-primary bg-opacity-50 navbar header">
            <Container fluid>
                <Navbar.Brand>
                    <Link to="/" className='site-logo h2'>PIVeiwer</Link>
                </Navbar.Brand>
                <Navbar id="basic-navbar-nav d-flex justify-content-end">
                    <Nav className='d-flex align-items-center'>
                        <NavDropdown title={<Image src={alarm} className='bg-danger rounded-circle'></Image>} id="basic-nav-dropdown" align="end">
                            <div>
                              <div className="small text-gray-500 text-center">공지판</div>
                            </div>
                            <NavDropdown.Item className='py-3'>
                                <div>
                                  <div className="small text-gray-500">Documentation</div>
                                  Usage instructions and reference
                                </div>
                            </NavDropdown.Item>
                        </NavDropdown>
                        <NavDropdown title={<Image src={logo} className='rounded-circle'></Image>} id="basic-nav-dropdown" align="end">
                            {/* 아직 기능 미구현 */}
                            <NavDropdown.Item href="#action/3.1" >내정보</NavDropdown.Item>
                            <NavDropdown.Divider />
                            <NavDropdown.Item onClick={Logout}>로그 아웃</NavDropdown.Item>
                        </NavDropdown>
                    </Nav>
                  </Navbar>
            </Container>
        </Navbar>
        ) : (
            <Navbar className="px-3 py-0 bg-primary bg-opacity-50 navbar header">
                <Container fluid>
                    <Navbar.Brand>
                        <Link to="/" className='site-logo h2'>PIVeiwer</Link>
                    </Navbar.Brand>
                    <Navbar id="basic-navbar-nav d-flex justify-content-end">
                        <Nav className='d-flex align-items-center'>
                            <Button type="button" href='/login'>login</Button>
                        </Nav>
                    </Navbar>
                </Container>
            </Navbar>
        )
    );
}

export default Header;

 

isLoggedIn 이 true 인 경우( 로그인 상태 )

 

isLoggedin 이 false 인 경우( 로그아웃 상태 ) 혹은 로그아웃 버튼을 눌렀을 경우

 


SideBar.js

function SideBar({setStreramTwitchId}) {
    const [followes, setFollowes] = useState([]);
    const navigate = useNavigate();

    // Link를 클릭하면 URL 경로와 함께 쿼리를 추가하여 다른 페이지로 이동합니다
    const handleLinkClick = (e, custom_url, video_id) => {
        e.preventDefault(); // 기본 동작 중지
        getLiveStreamTwitchChannelId(custom_url);
        const query = '?v=' + video_id;
        const newPath = '/'+ custom_url + query;
        navigate(newPath);
    }

    async function getFollowChannels() {
        try {
            const response = await fetch('http://localhost:8080/getFollowChannels', {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                    const responseData = await response.json();
                    setFollowes(responseData || []);
                    console.log('Spring 서버 응답 데이터:', responseData);
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생:', error);
        }
    }

    async function getLiveStreamTwitchChannelId(custom_url) {
        try {
            const response = await fetch('http://localhost:8080/getLiveStreamTwitchChannelId?custumUrl=' + custom_url, {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                    const responseData = await response.text();
                    setStreramTwitchId(responseData);
                    console.log('Spring 서버 응답 데이터:', responseData);
                    return responseData;
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생');
        }
    }

    useEffect(() => {
        // 초기 로딩 시 한 번 데이터 가져오기
        getFollowChannels();

        // 3초마다 데이터 업데이트
        const intervalId = setInterval(() => {
            getFollowChannels();
        }, 30000);

        return () => {
            // 컴포넌트가 언마운트될 때 clearInterval을 사용하여 인터벌 제거
            clearInterval(intervalId);
        };
    }, []);

    return (
        <div>
            <h4 className='m-0'>생방송</h4>
        {followes.map((item, index) => (
        <div key={index}>
            { item.is_live === "Live" && (
            <Link className="list-group-item list-group-item-action py-3 lh-sm" aria-current="true" onClick={(e) => handleLinkClick(e, item.custom_url, item.video_id)}>
            <div className='row'>
                <div className='sidebar_img'>
                    <img src={item.thumbnails_url} alt="" className="col sidebar_img"/>
                </div>
                <div className='col mx-2'>
                    <div className='overflow-text'><strong >{item.name}</strong></div>
                    <div><small className='live-text'>{item.is_live}</small></div>
                </div>
            </div>
            </Link>
            )}
        </div>
        ))}
        <h4 className='m-0'>오프라인</h4>
        {followes.map((item, index) => (
        <div key={index}>
            { item.is_live === "" && (
            <Link className="list-group-item list-group-item-action py-3 lh-sm" aria-current="true">
            <div className='row'>
                <div className='sidebar_img'>
                    <img src={item.thumbnails_url} alt="" className="col sidebar_img"/>
                </div>
                <div className='col mx-2'>
                    <div className='overflow-text'><strong >{item.name}</strong></div>
                    <div><small>{item.is_live}</small></div>
                </div>
            </div>
            </Link>
            )}
        </div>
        ))}
        </div>
    );
}


export default SideBar;

 

 

 


로그아웃 상태

로그인 상태(생방송 중, 비방상태)

SideBar.js에서 팔로우 추가를 입력했을때

Search.Results.js

function SearchResults() {
    const [customUrl, setCustomUrl] = useState('');
    const [searchResult, setSearchResult] = useState();
    const [show, setShow] = useState(false);

    const handleShow = () => {
        setShow(true);
    };

    const handleClose = () => setShow(false);

    async function searchChannel(customUrl) {
        try {
            const response = await fetch('http://localhost:8080/searchChannel?search=' + customUrl, {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                    const responseData = await response.json();
                    if(responseData.searchResult)
                        setSearchResult(responseData.liveConfig);
                    else
                        setSearchResult(null);
                    console.log('Spring 서버 응답 데이터:', responseData);
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생:', error);
        }
    }

    async function sendFollowData() {
        try {
            const response = await fetch('http://localhost:8080/follow?customUrl='+ customUrl, {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생:', error);
        }
    }

    const addFollow = () => {
        sendFollowData();
        handleClose();
    }

    return (
        <div className='h-100 p-5 w-50'>
            <h1>팔로우 추가</h1>
            <form onSubmit={(event) => {
                event.preventDefault();
                searchChannel(customUrl);
                }} className='pt-3 py-2'>
                <div className="row">
                    <div className="row w-100">
                        <label htmlFor="firstName" className="form-label">채널 검색</label>
                        <div className='col'>
                            <input type="text" className="form-control" placeholder="검색할 채널의 커스텀url을 정확히 입력해주세요" value={customUrl} onChange={(e) => setCustomUrl(e.target.value)}/>
                        </div>
                        <Button variant="primary" onClick={() => handleShow()} className='col addfollowbutton'>추가하기</Button>
                    </div>
                </div>
            </form>
            <div className="d-md-flex flex-md-equal w-100">
                <div className="text-bg-dark pt-3 w-100 overflow-hidden">
                    <div className="my-3 py-3">
                        <h2 className="display-5 text-center">검색결과</h2>
                    </div>
                    <div className="bg-body-tertiary shadow-sm mx-auto my-3 searchResult">
                        <div className="text-black searchResultField" id="youtubeResult" >
                            {searchResult != null ? (
                                <Link className="list-group-item py-3 px-2 lh-sm searchResultElement" aria-current="true">
                                    <div className='row'>
                                        <img src={searchResult.thumbnails_url} alt="Thumbnail" className='thumbnailSize'></img>
                                        <div className="w-100 align-items-center col">
                                            <div>
                                                <strong className="mb-1">{searchResult.custom_url}</strong>
                                            </div>
                                            <div className="d-flex justify-content-center align-items-center channel-des-area">{searchResult.description}</div>
                                        </div>
                                    </div>
                                </Link>
                            ) : (
                                <div className="list-group-item py-3 px-2 lh-sm searchResultElement">
                                    <div className='row'>
                                        <div className="w-100 align-items-center col">
                                            <div>
                                                <strong className="mb-1">검색결과가 없습니다.</strong>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            )}
                        </div>
                    </div>
                </div>
            </div>
            <Modal show={show} onHide={handleClose} className='disable-drag'>
                <Modal.Header closeButton>
                <Modal.Title>확인</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    <div className="row">
                        <div className="col">
                            <div className="mb-3">
                                <div>
                                    <div className='row'>
                                        <div>
                                        {searchResult != null ? (
                                            <Link className="list-group-item py-3 px-2 lh-sm searchResultElement" aria-current="true">
                                                <div className='row'>
                                                    <img src={searchResult.thumbnails_url} alt="Thumbnail" className='thumbnailSize'></img>
                                                    <div className="w-100 align-items-center col">
                                                        <div>
                                                            <strong className="mb-1">{searchResult.custom_url}</strong>
                                                        </div>
                                                        <div className="d-flex justify-content-center align-items-center channel-des-area">{searchResult.description}</div>
                                                    </div>
                                                </div>
                                            </Link>
                                        ) : (
                                            <div>검색을 진행한 뒤 진행해 주세요</div>
                                        )
                                        }
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </Modal.Body>
                <Modal.Footer>
                    <div className='d-flex align-items-center justify-content-between'>
                        확인이 끝났다면 추가하기 버튼을 눌러주세요. &nbsp;&nbsp;&nbsp;
                        <Button variant="primary" onClick={addFollow}>
                            추가하기
                        </Button>
                    </div>
                </Modal.Footer>
            </Modal>
        </div>
    );
}


export default SearchResults;

 

초기화면

 

검색전
검색후 예) 슈카 코믹스
추가하기 버튼 입력 ( 검색전 )

 

추가하기 버튼 입력 ( 검색후 )


MainContents.js

function InitForm() {
    return (
        <div className='col p-5'>
            로그인 초기 화면 입니다.
        </div>
    );
}

function NoticeBoard() {
    return (
      <div>
          <h1>공지판 입니다.</h1>
      </div>
    );
  }

function MainContents({ isLoggedIn, onLogin }) {
    return (
        isLoggedIn ? (
            <InitForm />
        ) : (
            <NoticeBoard />
        )
    );
}

export default MainContents;

로그인 전
로그인 후


생방송 중인 채널을 클릭시

초기 화면(예) 슈카월드 코믹스)

LiveStream.js

function LiveStream({streramTwitchId, setStreramTwitchId}) {
    const { customUrl } = useParams();
    const [twitchSearchResult, setTwitchSearchResult] = useState([]);
    const [show, setShow] = useState(false);
    const location = useLocation();
    const searchParams = new URLSearchParams(location.search);
    const videoId = searchParams.get('v');
    
    const handleShow = () => setShow(true);
    const handleClose = () => setShow(false);

    useEffect(() => {
        ytplayerResize();
        twitchChatIframeResize();
        window.addEventListener("ytplayer", ytplayerResize);
        window.addEventListener("twitchChatIframe", twitchChatIframeResize);

        // 컴포넌트가 언마운트될 때 이벤트 리스너 정리
        return () => {
            window.removeEventListener("ytplayer", ytplayerResize);
            window.removeEventListener("twitchChatIframe", twitchChatIframeResize);
        };
    }, []);

    async function setLiveStreamTwitchChannelId(e) {
        e.preventDefault();

        const twitchChannelId = document.getElementById('twitchId').value;
        try {
            const response = await fetch('http://localhost:8080/setLiveStreamTwitchChannelId?custumUrl=' + customUrl + "&twitchChannelId=" + twitchChannelId, {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                    const responseData = await response.text();
                    setStreramTwitchId(twitchChannelId);
                    console.log('Spring 서버 응답 데이터:', responseData);
                    return responseData;
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생');
        }
    }

    async function searchTwitchChannelId(search) {
        try {
            const response = await fetch('http://localhost:8080/twitchSearchChannel?search=' + search, {
                method: 'GET',
                headers: {
                'Content-Type': 'application/json',
                },
                credentials: 'include',
            });
                if (response.ok) {
                    const responseData = await response.json();
                    setTwitchSearchResult(responseData.data || []);
                    console.log('Spring 서버 응답 데이터:', responseData);
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
            console.error('오류 발생:', error);
        }
    }

    function ytplayerResize() {
        const iframe = document.getElementById("ytplayer");
        // 필요한 경우 헤더와 푸터의 높이를 뺄 수 있습니다
        const headerHeight = 100; // 헤더 높이로 대체
        const footerHeight = 0; // 푸터 높이로 대체

        const sideBarWidth = 250;
        const chattingWidth = 350;

        iframe.width = window.innerWidth - sideBarWidth - chattingWidth;
        iframe.height = window.innerHeight - headerHeight - footerHeight;
    }

    function twitchChatIframeResize() {
        const iframe = document.getElementById("twitchChatIframe");
        // 필요한 경우 헤더와 푸터의 높이를 뺄 수 있습니다
        const headerHeight = 100; // 헤더 높이로 대체

        // iframe.width = window.innerWidth - sideBarWidth - chattingWidth;
        iframe.height = window.innerHeight - headerHeight;
    }

    return (
        <div className='w-100 h-100 row'>
            <iframe 
                id="ytplayer" 
                title="liveStream"
                src={"https://www.youtube.com/embed/" + videoId + "?autoplay=1"}
                frameborder="0"
                allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                allowfullscreen
                className="col-md-8"
                >
            </iframe>
            {/* <iframe width="560" height="315" src="https://www.youtube.com/embed/DgpEmYsT9hE?si=NJmaumU3EIGVDXzi" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> */}
            <div className='col-md-3'>
                <div className='streamIdBx'>
                    <button onClick={() => handleShow()}>Twitch id 찾기</button>
                </div>
                <iframe
                    src={"https://www.twitch.tv/embed/"+ streramTwitchId +"/chat?parent=localhost"}
                    title="twitchChatIframe"
                    width="350"
                    id="twitchChatIframe"
                    theme="black"
                >
                </iframe>
            </div>
            <Modal show={show} onHide={handleClose} className='disable-drag'>
                <Modal.Header closeButton>
                <Modal.Title>twitch id 찾기</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    <label>직접 입력하거나 검색해서 찾아주세요</label>
                    <input type="text" className="form-control" id="twitchId" placeholder="twitch id"></input>
                    <div id="searchResult"></div>
                    <div className='pt-4'>
                        <label>검색</label>
                        <form onSubmit={(e) => {
                            e.preventDefault();
                            searchTwitchChannelId(e.target.elements.searchInput.value);}}>
                            <input type="text" className="form-control" placeholder="Search..." name="searchInput"/>
                        </form>
                        <div>검색 결과</div>
                        <div className='scrollarea search-result'>
                        {twitchSearchResult.map((item, index) => (
                            <Link key={index} className="list-group-item py-3 px-2 lh-sm searchResultElement" aria-current="true" onClick={(e) => {e.preventDefault(); document.getElementById("twitchId").value = item.broadcaster_login}}>
                                <div className='row scrollba'>
                                    <img src={item.thumbnail_url} alt={item.thumbnail_url ? "Thumbnail" : ""} className="thumbnailSize" />
                                    <div className="w-100 align-items-center col">
                                        <div>
                                            <strong className="mb-1">{item.display_name}</strong>
                                        </div>
                                    </div>
                                </div>
                            </Link>
                        ))}
                        </div>
                    </div>
                </Modal.Body>
                <Modal.Footer>
                    <Button variant="primary" onClick={setLiveStreamTwitchChannelId}>
                        추가하기
                    </Button>
                </Modal.Footer>
            </Modal>
        </div>
    );
}

export default LiveStream;

 

동영상 영역

채팅 영역



트위치 방송 아이디가 없는 경우


트위치 방송 아이디를 입력했는 경우

 

Twitch Id 찾기 버튼을 누른경우

 

다이어로그로 표현 ( 트위치 아이디 검색전 )
다이어로그로 표현 ( 트위치 아이디 검색후 )


헤더의 Login 버튼을 클릭 했을 경우

로그인 화면

Login.js

function SignupForm(){
    const Signup = () => {
        const data = {
            username: document.getElementById('username').value,
            password: document.getElementById('password').value,
            youtubeChannelId: document.getElementById('youtubeChannelId').value,
        };
        // Spring 서버로 JSON 데이터를 보내는 함수
        async function sendDataToSpringServer() {
        try {
            const response = await fetch('http://localhost:8080/signup', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json', // JSON 데이터라는 것을 명시
                },
                body: JSON.stringify(data), // JSON 데이터를 문자열로 변환해서 요청 본문에 담음
            });
        
            if (response.ok) {
                const responseData = await response.json(); // Spring 서버에서의 응답 데이터를 JSON으로 파싱
                console.log('Spring 서버 응답 데이터:', responseData);
            } else {
                console.error('Spring 서버 응답 에러:', response.status, response.statusText);
            }
        } catch (error) {
            console.error('오류 발생:', error);
        }
      }
      sendDataToSpringServer();
    }

    return (
        <div className='form-signin col p-5 mx-auto'>
            <div className='text-center h3 p-3'>회원 가입</div>
            <EmailForm/>
            <PasswordForm/>
            <div className="form-floating mt-2">
                <input type="text" className="form-control" id="youtubeChannelId" placeholder=""></input>
                <label htmlFor="youtubeChannelid">YoutubeChannelid</label>
            </div>
            <button onClick={Signup} className="btn btn-primary w-100 py-2 mt-3">Signin</button>
        </div>
    );
}

function EmailForm(){
    return (
        <div className="form-floating">
            <input type="text" className="form-control email-signin" id="username" placeholder=""></input>
            <label htmlFor="username">Email address</label>
        </div>
    );
}

function PasswordForm(){
    return (
        <div className="form-floating">
            <input type="password" className="form-control password-signin" id="password" placeholder=""></input>
            <label htmlFor="password">password</label>
        </div>
    );
}

function Login({ onLogin }) {
    const [isSignUp, setIsSignUp] = useState(true);
    const navigate  = useNavigate();

    const GoSignup = () => {
        setIsSignUp(false);
    };

    const LoginSubmit = () =>{
        const data = {
            username: document.getElementById('username').value,
            password: document.getElementById('password').value
        };
        // Spring 서버로 JSON 데이터를 보내는 함수
        async function sendDataToSpringServer() {
            try {
                const response = await fetch('http://localhost:8080/login', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json', // JSON 데이터라는 것을 명시
                    },
                    body: JSON.stringify(data), // JSON 데이터를 문자열로 변환해서 요청 본문에 담음
                    credentials: 'include',
                });
            
                if (response.ok) {
                    const responseData = await response.json(); // Spring 서버에서의 응답 데이터를 JSON으로 파싱
                    // 로그인 확인 함수 실행
                    onLogin();
                    navigate(`/`);
                    console.log('Spring 서버 응답 데이터:', responseData);
                } else {
                    console.error('Spring 서버 응답 에러:', response.status, response.statusText);
                }
            } catch (error) {
                console.error('오류 발생:', error);
            }
        }
        sendDataToSpringServer();
    }
    return (
        isSignUp ? (
            <React.StrictMode>
                <div className='form-signin col p-5 mx-auto'>
                    <div className='text-center h3 p-3'>로그인</div>
                    <EmailForm />
                    <PasswordForm />
                    <button onClick={LoginSubmit} className="btn btn-primary w-100 py-2 mt-3">Login</button>
                    <button onClick={GoSignup} className="btn btn-primary w-100 py-2 mt-3">Signin</button>
                </div>
            </React.StrictMode>
        ) : (
            <React.StrictMode>
                <SignupForm />
            </React.StrictMode>
        )
    );
}

export default Login;

 

Signin 버튼을 누른 경우

회원가입 폼

 

반응형

+ Recent posts