아이돌 하이라이트 추출기 만들다가 아이돌 스케쥴표 만든 건에 대하여

이전 게시글: 라이브아이돌 영상 추출 도전하기 2번째 도전

역시 코딩 에러 해결은 어렵다

5주간의 AI 캠프를 진행하며 느낀 점 입니다.
이전 게시글에서 아이돌 영상에서 각 멤버 별 추적까지는 성공했지만 연속적인 영상을 생성하는데서 애를 먹었습니다.

이에 시도해본 부분은 더 좋은 영상 객체인식 모델 (YOLOv8x) 를 사용하고 칼만필터 적용을 해봤지만 결과는 마찬가지였습니다. 아이돌 멤버 자체를 학습시켜서 다시 시도를 해봐야겠다는 생각이 들었습니다.

이번 주차에서는 아이돌 하이라이트 추출기 마무리를 짓지 못했는데요 문제해결 관련 이슈도 있었지만 사실은 만들었던 라이브아이돌 커뮤니티 관련해서 협업을 진행할 일이 생겼습니다.

라이브아이돌과 협업한다.

저는 디지털 전환도 좋아하지만 오프라인으로 빠르게 시도하는 것도 좋아합니다. 서비스 론칭과 동시에 각 아이돌과 소속사에 전부 웹페이지와 업무자동화를 도와준다고 연락을 돌렸고, 운이 좋게도 몇 개의 팀과 얘기를 나눌 수 있었습니다. 그 중에서 공통적으로 나온 고객의 목소리는 스케쥴표 확인이 너무 힘들다 였습니다.

그래서 아이돌 스케쥴표 만들거다. (클로드가)

우선 접근은 긍정적인 UX(유저의경험)를 위해서 각자의 권한이나 아이디를 별도로 부여하는 것보다 주최측이 대표로 팀들을 멀티셀렉으로 선택해서 공연정보를 넣고 설문을 제출하면 제 서비스 화면에서 캘린더가 자동으로 완성되는 형태를 생각했습니다.

이 부분은 간단하게 에어테이블을 사용했습니다. (에어테이블이 궁금하시다면 댓글 남겨주세요!)

이후에는 예쁘고 편한 캘린더를 만들기위해 프론트엔드에서 사용되는 react의 캘린더 라이브러리를 사용하고자 했는데요, 클로드 선생님께 아래와 같이 프롬프트를 입력했습니다.

너는 세계 최고의 프로덕트 오너이고, 아래는 DB 정보이다.

공연명: 공연의 이름이나 제목 정보를 저장하는 싱글텍스트 필드

공연 날짜: 공연이 열리는 날짜 정보를 저장하는 필드

참여 팀: 공연에 참여하는 팀이나 그룹의 정보를 저장하는 멀티 셀렉트 필드

포스터: 공연 포스터 이미지를 저장하는 필드

주최 팀: 공연을 주최하는 팀이나 단체의 정보를 저장하는 싱글텍스트 필드

예매 링크: 공연 티켓을 예매할 수 있는 웹페이지 url 링크를 저장하는 필드

장소: 공연의 잔여 좌석 정보를 저장하는 싱글셀렉트 필드

위 정보를 바탕으로 공연명을 기본 캘린더에서 보여주고 클릭 했을 때, 모달팝업으로 각 필드의 상세 값을 보여줄 수 있게 하려고하는데, 리액트 기준으로 구체적이고 자세하게 수치를 넣어서 바로 복사 붙여넣기해도 작동하도록 주석을 넣어가며 쉽게 설명해줘

처음에는 codepen 을 사용하였고 이에따라 HTML, CSS, JSX 코드를 주었는데요. 터미널을 사용해야하는 라이브러리 설치에 애를 먹어서 질문을 코드블럭에 삽입할 것 이라고 변경했습니다.

→ 이렇게하면 별도 설치없이 CDN 링크로 script 코드가 로딩될 때 자동으로 불러오는 것으로 이해했습니다.

하지만 실패를 했는데요 사실 아지 원인은 잘 모르겠습니다. 아래는 시도한 코드

<!DOCTYPE html>
<html>
<head>
  <title>아이돌 공연 일정</title>
  <!-- 리액트 라이브러리 로드 -->
  <script src="https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"></script>
  <!-- 바벨 스크립트 로드 (JSX 문법 사용을 위해) -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <!-- Axios 라이브러리 로드 (에어테이블 API 호출을 위해) -->
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <!-- 리액트 캘린더 라이브러리 로드 -->
  <script src="https://cdn.jsdelivr.net/npm/react-calendar/dist/Calendar.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/react-calendar/dist/Calendar.css" />
  <!-- 리액트 모달 라이브러리 로드 -->
  <script src="https://cdn.jsdelivr.net/npm/react-modal/dist/react-modal.min.js"></script>
  <style>
    /* 캘린더 스타일 */
    .react-calendar {
      width: 100%;
      max-width: 800px;
      margin: 0 auto;
    }
    /* 모달 스타일 */
    .modal {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      max-width: 400px;
      width: 100%;
      text-align: center;
    }
    .modal img {
      max-width: 100%;
      height: auto;
      margin-bottom: 10px;
    }
    .modal h2 {
      font-size: 24px;
      margin-bottom: 10px;
    }
    .modal p {
      margin-bottom: 5px;
    }
    .modal a {
      display: inline-block;
      background-color: #007bff;
      color: white;
      text-decoration: none;
      padding: 8px 16px;
      border-radius: 4px;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    const { useState, useEffect } = React;

    const PerformanceCalendar = () => {
      const [events, setEvents] = useState([]);
      const [selectedEvent, setSelectedEvent] = useState(null);
      const [isModalOpen, setIsModalOpen] = useState(false);

      useEffect(() => {
        // 에어테이블에서 데이터 가져오기
        const fetchEvents = async () => {
          const response = await axios.get('https://api.airtable.com/v0/appBylPCnkzBr9WLo/idol_schedule', {
            headers: {
              Authorization: 'Bearer patVS7Qqb3eDpzMqP',
            },
          });

          const records = response.data.records;
          const formattedEvents = records.map((record) => ({
            id: record.id,
            title: record.fields['공연명'],
            start: new Date(record.fields['공연 날짜']),
            ...record.fields,
          }));

          setEvents(formattedEvents);
        };

        fetchEvents();
      }, []);

      const openModal = (event) => {
        setSelectedEvent(event);
        setIsModalOpen(true);
      };

      const closeModal = () => {
        setSelectedEvent(null);
        setIsModalOpen(false);
      };

      return (
        <div>
          <h1>아이돌 공연 일정</h1>
          {/* 리액트 캘린더 컴포넌트 */}
          <Calendar
            tileContent={({ date, view }) => {
              // 해당 날짜에 해당하는 공연이 있는지 확인
              const eventOnDate = events.find(
                (event) => event.start.toDateString() === date.toDateString()
              );
              // 공연이 있으면 공연명을 표시
              return eventOnDate ? <div>{eventOnDate.title}</div> : null;
            }}
            onClickDay={(date) => {
              // 클릭한 날짜에 해당하는 공연이 있는지 확인
              const eventOnDate = events.find(
                (event) => event.start.toDateString() === date.toDateString()
              );
              // 공연이 있으면 모달 열기
              if (eventOnDate) {
                openModal(eventOnDate);
              }
            }}
          />

          {/* 리액트 모달 컴포넌트 */}
          {isModalOpen && (
            <div className="modal">
              <h2>{selectedEvent.title}</h2>
              <img src={selectedEvent['포스터'][0].url} alt="포스터" />
              <p>날짜: {selectedEvent.start.toLocaleDateString()}</p>
              <p>참여 팀: {selectedEvent['참여 팀'].join(', ')}</p>
              <p>주최 팀: {selectedEvent['주최 팀']}</p>
              <p>장소: {selectedEvent['장소']}</p>
              <a href={selectedEvent['예매 링크']} target="_blank" rel="noopener noreferrer">
                예매하기
              </a>
              <button onClick={closeModal}>닫기</button>
            </div>
          )}
        </div>
      );
    };

    ReactDOM.render(<PerformanceCalendar />, document.getElementById('root'));
  </script>
</body>
</html>

웬만하면 개발이 재밌어서 마무리를 했을텐데, 당일 미팅이라 시간이 없어 에어테이블+재피어+노션+우피로 구현내서 완성본을 iframe으로 제 서비스에 심었습니다. 아래는 결과화면 입니다.

결과링크는 여기서 보실 수 있어요

저희는 개발캠프지만 간단하게 노코드로 구동한 내용 공유드립니다.

끝.

#AI문과생

11
5개의 답글

(채용) 콘텐츠 마케터, AI 엔지니어, 백엔드 개발자

지피터스의 수 천개 AI 활용 사례 데이터를 AI로 재가공 할 인재를 찾습니다

👉 이 게시글도 읽어보세요