├── .eslintrc.json ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierrc ├── CNAME ├── README.md ├── components ├── 2025-commingsoon │ ├── constants │ │ └── data.ts │ ├── sections │ │ ├── Footer.tsx │ │ ├── Hero.tsx │ │ ├── Introduce.tsx │ │ ├── Overview.tsx │ │ ├── SNS.tsx │ │ └── Sponsor.tsx │ └── shared │ │ ├── Card │ │ └── OverviewCard.tsx │ │ ├── Countdown │ │ ├── CountdownItem.tsx │ │ └── CountdownSeparator.tsx │ │ ├── Header │ │ ├── ArchiveMenu.tsx │ │ ├── Header.tsx │ │ ├── Sidebar.tsx │ │ └── SidebarAccordionItem.tsx │ │ ├── Marquee │ │ ├── DesktopMarquee.tsx │ │ ├── MarqueeWrapper.tsx │ │ ├── MobileMarquee.tsx │ │ └── TabletMarquee.tsx │ │ ├── MetaHead.tsx │ │ ├── Modal │ │ └── PrivacyModal.tsx │ │ └── Typography │ │ └── ScrambleText.tsx ├── first │ ├── Banner.tsx │ ├── CodeOfConduct.tsx │ ├── FAQ.tsx │ ├── Footer.tsx │ ├── Gallery.tsx │ ├── Header.tsx │ ├── Hero.tsx │ ├── Introduce.tsx │ ├── Map.tsx │ ├── NameCard.tsx │ ├── NetWorking.tsx │ ├── PrivacyModal.tsx │ ├── Program.tsx │ ├── SNS.tsx │ ├── Sidebar.tsx │ ├── Sponsor.tsx │ ├── Staff.tsx │ ├── common │ │ ├── FAQField.tsx │ │ ├── KakaoMap.tsx │ │ ├── MetaHead.tsx │ │ ├── StaffCard.tsx │ │ └── SwiperSlide.tsx │ ├── gallery │ │ └── NavButton.tsx │ └── introduce │ │ └── Talk.tsx ├── second │ ├── Banner.tsx │ ├── CodeOfConduct.tsx │ ├── FAQ.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── Hero.tsx │ ├── Introduce.tsx │ ├── Map.tsx │ ├── NameCard.tsx │ ├── NetWorking.tsx │ ├── PrivacyModal.tsx │ ├── Program.tsx │ ├── SNS.tsx │ ├── Sidebar.tsx │ ├── Sponsor.tsx │ ├── Staff.tsx │ ├── common │ │ ├── FAQField.tsx │ │ ├── KakaoMap.tsx │ │ ├── MetaHead.tsx │ │ ├── SectionTab.tsx │ │ ├── StaffCard.tsx │ │ └── SwiperSlide.tsx │ ├── gallery │ │ └── NavButton.tsx │ └── introduce │ │ └── Talk.tsx └── third │ ├── ApplyAndSNS.tsx │ ├── Banner.tsx │ ├── CodeOfConduct.tsx │ ├── FAQ.tsx │ ├── Footer.tsx │ ├── Header │ ├── PassedConfMenu.tsx │ └── index.tsx │ ├── Hero.tsx │ ├── Introduce.tsx │ ├── Map.tsx │ ├── NameCard.tsx │ ├── NetWorking.tsx │ ├── PrivacyModal.tsx │ ├── Program.tsx │ ├── Session.tsx │ ├── SideBar │ ├── SideBarItem.tsx │ └── index.tsx │ ├── Sponsor.tsx │ ├── Staff.tsx │ ├── Timetable.tsx │ ├── common │ ├── FAQField.tsx │ ├── KakaoMap.tsx │ ├── MetaHead.tsx │ ├── SectionTab.tsx │ ├── StaffCard.tsx │ └── SwiperSlide.tsx │ ├── gallery │ └── NavButton.tsx │ └── introduce │ └── Talk.tsx ├── data ├── 2025 │ └── navigation.ts ├── first │ ├── FAQ.ts │ ├── Staff.ts │ └── Talk.ts ├── second │ ├── FAQ.ts │ ├── Staff.ts │ └── Talk.ts └── third │ ├── EventDetails.ts │ ├── FAQ.ts │ ├── Links.ts │ ├── Menu.ts │ ├── Session.ts │ ├── Sponsor.ts │ ├── Staff.ts │ ├── Talk.ts │ └── Timetable.ts ├── lib └── observer.ts ├── next.config.js ├── package.json ├── pages ├── 2025 │ └── index.tsx ├── _app.tsx ├── _document.tsx ├── api │ └── hello.ts ├── first │ └── index.tsx ├── index.tsx ├── second │ └── index.tsx └── third │ └── index.tsx ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── fonts │ ├── AbhayaLibre-Bold.ttf │ ├── Pretendard-Black.subset.woff2 │ ├── Pretendard-Bold.subset.woff2 │ ├── Pretendard-ExtraBold.subset.woff2 │ ├── Pretendard-ExtraLight.subset.woff2 │ ├── Pretendard-Light.subset.woff2 │ ├── Pretendard-Medium.subset.woff2 │ ├── Pretendard-Regular.subset.woff2 │ ├── Pretendard-SemiBold.subset.woff2 │ └── Pretendard-Thin.subset.woff2 └── images │ ├── 2025 │ ├── assets │ │ ├── Group 101.svg │ │ ├── Group 87.png │ │ ├── Group 87.svg │ │ ├── Star 1.svg │ │ ├── Vector-1.svg │ │ ├── Vector-2.svg │ │ ├── Vector-3.svg │ │ ├── Vector.svg │ │ └── logo │ │ │ ├── elice-logo.svg │ │ │ └── logo.svg │ ├── background │ │ ├── commingsoon-bg.png │ │ ├── commingsoon-mobile-bg.png │ │ ├── elicelap-bg.png │ │ └── elicelap-mobile-bg.png │ └── overview │ │ ├── card_default_img.png │ │ └── teo-conf-2025-staff-1-3.jpeg │ ├── ArrowLeft.svg │ ├── ArrowRight.svg │ ├── Banner_2.svg │ ├── Behance.svg │ ├── BehancePurple.svg │ ├── DoubleDownArrow.svg │ ├── Down_1.svg │ ├── Down_2.svg │ ├── GithubLogo.svg │ ├── GithubLogoPurple.svg │ ├── GithubLogoWhite.svg │ ├── HHPlusLogo.svg │ ├── Instagram.svg │ ├── Instagram_2.svg │ ├── Introduce1.svg │ ├── Introduce1_2.svg │ ├── Introduce1_3.svg │ ├── Introduce2.svg │ ├── Introduce2_2.svg │ ├── Introduce2_3.svg │ ├── Introduce3.svg │ ├── Introduce3_2.svg │ ├── Introduce3_3.svg │ ├── Introduce4.svg │ ├── Introduce4_2.svg │ ├── Introduce4_3.svg │ ├── JumpitLogo.svg │ ├── LinkedIn.svg │ ├── LinkedIn_2.svg │ ├── LogoWhite_1.svg │ ├── Logo_1.png │ ├── Logo_2.svg │ ├── Logo_3.svg │ ├── Logo_primary_3.svg │ ├── Mouse_1.svg │ ├── Mouse_2.svg │ ├── Mouse_3.svg │ ├── NameCard_1.png │ ├── NameCard_2.jpg │ ├── NameCard_3.svg │ ├── Networking1.svg │ ├── Networking2.svg │ ├── Networking3.svg │ ├── Networking_2_1.svg │ ├── Networking_2_2.svg │ ├── Networking_2_3.svg │ ├── Networking_3_1.svg │ ├── Networking_3_2.svg │ ├── Networking_3_3.svg │ ├── OgImage.png │ ├── QR.svg │ ├── SampleAnimal.png │ ├── SpeakerPrimary_3.svg │ ├── SpeakerPurple.svg │ ├── SpeakerWhite.svg │ ├── SpeakerWhite_3.svg │ ├── Subtract.svg │ ├── TabletBI1_2.svg │ ├── TabletBI1_3.svg │ ├── TabletBI2_2.svg │ ├── TabletBI2_3.svg │ ├── TeoBI.png │ ├── TeoBI2.png │ ├── Twitter.svg │ ├── Twitter_2.svg │ ├── UpArrow.svg │ ├── UpArrow_2.svg │ ├── UpArrow_3.svg │ ├── UpArrow_4.svg │ ├── X.svg │ ├── favicon │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── apple-icon-precomposed.png │ ├── apple-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── manifest.json │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ └── ms-icon-70x70.png │ ├── red_2.svg │ ├── red_3.svg │ ├── sketch │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.png │ ├── 20.png │ ├── 21.png │ ├── 22.png │ ├── 23.png │ ├── 24.png │ ├── 25.png │ ├── 26.png │ ├── 27.png │ ├── 28.png │ ├── 29.png │ ├── 3.png │ ├── 30.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ ├── speakers │ ├── chedar.png │ ├── derrick.png │ ├── moseung.png │ ├── nagnae.png │ ├── parang.png │ ├── teo.png │ └── third │ │ ├── AndrewYu.jpeg │ │ ├── Byeongsker.jpg │ │ ├── Donghun.jpg │ │ ├── Happy.jpg │ │ ├── Hyan.jpg │ │ ├── Kimcheomji.jpg │ │ └── Kimsan.jpg │ ├── staff │ ├── Anna.png │ ├── Domo.png │ ├── Halang.png │ ├── Hubble.png │ ├── J.png │ ├── Juni.png │ ├── Mini.png │ ├── Moseung.png │ ├── Raul.png │ ├── Rookie.png │ ├── Sudal.png │ ├── Teo.png │ ├── Three.png │ └── third │ │ ├── Casey.jpg │ │ ├── Field.jpg │ │ ├── Jyan.jpg │ │ ├── Mincho.jpg │ │ ├── Seoltang.jpg │ │ ├── Solssak.jpg │ │ └── Sooya.jpg │ ├── yellow_2.svg │ └── yellow_3.svg ├── styles └── globals.css ├── tailwind.config.ts ├── tsconfig.json ├── types ├── custom.d.ts └── index.tsx ├── utils ├── path.ts └── scroll.ts └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "prettier", "next/core-web-vitals"], 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": "error" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Key Changes 2 | 3 | - [x] 4 | 5 | ## To Reviewers 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | www.teoconf.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Run the development server 2 | 3 | ```bash 4 | yarn 5 | yarn dev 6 | ``` 7 | 8 | ### Commit Message 9 | 10 | - `feat`: 새로운 기능 추가 11 | 12 | - `design`: css 변경 등 사용자 UI 디자인 수정 13 | 14 | - `fix`: 버그 수정 15 | 16 | - `refactor`: 리팩토링 17 | 18 | - `chore`: package 매니저 설정, deploy script 수정 등 19 | 20 | - `docs`: 문서 수정 21 | 22 | - `comment`: 주석 추가 23 | 24 | - `rename`: 파일 이름 변경 25 | 26 | - `remove`: 파일 삭제 27 | -------------------------------------------------------------------------------- /components/2025-commingsoon/sections/Footer.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import Image from 'next/image' 3 | 4 | import UpArrow from '@/public/images/UpArrow_4.svg' 5 | import PrivacyModal from '../shared/Modal/PrivacyModal' 6 | import Logo from '@/public/images/Logo_2.svg' 7 | import { scrollToTop } from '@/utils/scroll' 8 | 9 | const Footer = () => { 10 | const [isModalOpen, setIsModalOpen] = useState(false) 11 | 12 | const handlePrivacyClick = () => { 13 | setIsModalOpen((prev) => !prev) 14 | } 15 | 16 | return ( 17 | 59 | ) 60 | } 61 | 62 | export default Footer 63 | -------------------------------------------------------------------------------- /components/2025-commingsoon/sections/Introduce.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import { getPath } from '@/utils/path' 3 | // test 4 | const Introduce = () => { 5 | return ( 6 |
7 |
8 | {/* Talk, Experience, Openly */} 9 |

10 | Talk, Experience, Openly 11 |

12 | 13 | {/* 테두리 없이, 오늘의 경험을 나누는 놀이터 */} 14 |

15 | 16 | 두리 없이, 17 | 늘의 경험을 나누는 놀이터 18 |

19 | 20 | {/* 솔직한 당신의 경험이 누군가의 영감이 되는 네트워킹 공간에 초대합니다! */} 21 |
22 |

23 | 솔직한 당신의 경험이 24 |
25 | 26 | {' '} 27 | 누군가의 영감이 되는 28 | 29 |
30 | 네트워킹 공간에 초대합니다! 31 |

32 |
33 |
34 | 35 | {/* 캐릭터 일러스트 */} 36 |
37 | character 44 |
45 |
46 | ) 47 | } 48 | 49 | export default Introduce 50 | -------------------------------------------------------------------------------- /components/2025-commingsoon/sections/Overview.tsx: -------------------------------------------------------------------------------- 1 | import OverviewCard from '../shared/Card/OverviewCard' 2 | import MarqueeWrapper from '../shared/Marquee/MarqueeWrapper' 3 | import { Overview as OverviewData } from '../constants/data' 4 | 5 | const Overview = () => { 6 | return ( 7 |
11 |
12 | {/* 제목 */} 13 |
14 |

15 | 테오콘 모아보기 16 |

17 | 18 | 19 | 역대 테오콘이 궁금하다면?
20 | 아래 글들을 확인해보세요! 21 |
22 |
23 | 24 | {/* 내용 영역 */} 25 | 26 | {OverviewData.map((item, index) => ( 27 |
31 | 32 |
33 | ))} 34 |
35 |
36 |
37 | ) 38 | } 39 | 40 | export default Overview 41 | -------------------------------------------------------------------------------- /components/2025-commingsoon/sections/SNS.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | 4 | import LinkedIn from '@/public/images/LinkedIn.svg' 5 | import Instagram from '@/public/images/Instagram.svg' 6 | import X from '@/public/images/X.svg' 7 | 8 | const SNS = () => ( 9 |
10 |
11 |

12 | 테오콘 소식을 더 빨리 알고 싶다면? 13 |

14 |
15 | 21 | linkedin 바로가기 28 | 29 | 35 | X 바로가기 42 | 43 | 49 | instagram 바로가기 56 | 57 |
58 |
59 |
60 | ) 61 | 62 | export default SNS 63 | -------------------------------------------------------------------------------- /components/2025-commingsoon/sections/Sponsor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import { getPath } from '@/utils/path' 4 | 5 | const Sponsor = () => { 6 | return ( 7 | 60 | ) 61 | } 62 | 63 | export default Sponsor 64 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Card/OverviewCard.tsx: -------------------------------------------------------------------------------- 1 | import { getPath } from '@/utils/path' 2 | import Image from 'next/image' 3 | 4 | interface OverviewCardProps { 5 | title: string 6 | description: string 7 | thumbnail: string 8 | link: string 9 | nickname?: string 10 | } 11 | 12 | const OverviewCard = ({ 13 | title, 14 | description, 15 | thumbnail, 16 | link, 17 | nickname, 18 | }: OverviewCardProps) => { 19 | const defaultThumbnail = getPath('/images/2025/overview/card_default_img.png') 20 | const imageSrc = thumbnail || defaultThumbnail 21 | 22 | // PC: 360:460 비율 유지 (360/460 ≈ 0.783) 23 | // 모바일 320px → 높이 409px (320/0.783 ≈ 409) 24 | // 태블릿 400px → 높이 511px (400/0.783 ≈ 511) 25 | // PC 360px → 높이 460px (360/0.783 ≈ 460) 26 | 27 | return ( 28 | 34 | {/* 썸네일 이미지 */} 35 |
36 | {title} 37 |
38 | 39 | {/* 타이틀 및 디스크립션 */} 40 |
41 |

42 | {title} 43 |

44 | {nickname && ( 45 |

46 | {nickname} 47 |

48 | )} 49 |

50 | {description} 51 |

52 |
53 |
54 | ) 55 | } 56 | 57 | export default OverviewCard 58 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Countdown/CountdownItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface CountdownItemProps { 4 | value: string 5 | label: string 6 | textColor?: string 7 | textSize?: string 8 | labelColor?: string 9 | labelSize?: string 10 | } 11 | 12 | const CountdownItem: React.FC = ({ 13 | value, 14 | label, 15 | textColor = 'text-white', 16 | textSize = 'text-[3rem] tablet:text-[5rem]', 17 | labelColor = 'text-[#D1D5D8]', 18 | labelSize = 'text-[1.25rem]', 19 | }) => { 20 | const textHeight = textSize.includes('2rem') 21 | ? 'h-[2rem] leading-[2rem]' 22 | : 'h-[3rem] tablet:h-[5rem] leading-[3rem] tablet:leading-[5rem]' 23 | 24 | return ( 25 |
26 |
29 | {value} 30 |
31 |
34 | {label} 35 |
36 |
37 | ) 38 | } 39 | 40 | export default CountdownItem 41 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Countdown/CountdownSeparator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface CountdownSeparatorProps { 4 | textColor?: string 5 | textSize?: string 6 | } 7 | 8 | const CountdownSeparator = ({ 9 | textColor = 'text-white', 10 | textSize = 'text-[3rem] tablet:text-[5rem]', 11 | }: CountdownSeparatorProps) => { 12 | const textHeight = textSize.includes('2rem') 13 | ? 'h-[2rem] leading-[2rem]' 14 | : 'h-[3rem] tablet:h-[5rem] leading-[3rem] tablet:leading-[5rem]' 15 | 16 | return ( 17 |
20 | : 21 |
22 | ) 23 | } 24 | 25 | export default CountdownSeparator 26 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import Image from 'next/image' 3 | import { useRouter } from 'next/router' 4 | import MenuIcon from '@mui/icons-material/Menu' 5 | import CloseIcon from '@mui/icons-material/Close' 6 | 7 | import Logo from '@/public/images/2025/assets/logo/logo.svg' 8 | import Sidebar from './Sidebar' 9 | import ArchiveMenu from './ArchiveMenu' 10 | import { scrollToSection } from '@/utils/scroll' 11 | 12 | export const Header = () => { 13 | const router = useRouter() 14 | const [isOpen, setIsOpen] = useState(false) 15 | 16 | const toggleSide = () => { 17 | setIsOpen((prev) => !prev) 18 | } 19 | 20 | return ( 21 |
25 | 68 |
69 | ) 70 | } 71 | 72 | export default Header 73 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Header/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { scrollToSection } from '@/utils/scroll' 3 | import { ARCHIVE_ITEMS, SIDEBAR_ITEMS } from '@/data/2025/navigation' 4 | import SidebarAccordionItem from './SidebarAccordionItem' 5 | 6 | interface SidebarProps { 7 | isOpen: boolean 8 | setIsOpen: React.Dispatch> 9 | } 10 | 11 | const Sidebar = ({ isOpen, setIsOpen }: SidebarProps) => { 12 | if (!isOpen) return null 13 | 14 | return ( 15 |
{ 18 | if (e.target === e.currentTarget) { 19 | setIsOpen(false) 20 | } 21 | }} 22 | > 23 |
24 | {SIDEBAR_ITEMS.map((item, idx) => ( 25 |
26 | 36 |
37 | ))} 38 |
39 | setIsOpen(false)} 43 | /> 44 |
45 |
46 |
47 | ) 48 | } 49 | 50 | export default Sidebar 51 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Header/SidebarAccordionItem.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { useRouter } from 'next/router' 3 | import { KeyboardArrowUp } from '@mui/icons-material' 4 | import clsx from 'clsx' 5 | 6 | interface ArchiveItem { 7 | name: string 8 | path: string 9 | } 10 | 11 | interface SidebarAccordionItemProps { 12 | title: string 13 | items: ArchiveItem[] 14 | onNavigate: () => void 15 | } 16 | 17 | const SidebarAccordionItem = ({ 18 | title, 19 | items, 20 | onNavigate, 21 | }: SidebarAccordionItemProps) => { 22 | const router = useRouter() 23 | const [isOpen, setIsOpen] = useState(false) 24 | 25 | const handleNavigation = (path: string) => { 26 | router.push(path) 27 | onNavigate() 28 | } 29 | 30 | return ( 31 | <> 32 |
33 | 40 | 52 |
53 |
59 | {items.map((item, idx) => ( 60 |
61 | 68 |
69 | ))} 70 |
71 | 72 | ) 73 | } 74 | 75 | export default SidebarAccordionItem 76 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Marquee/DesktopMarquee.tsx: -------------------------------------------------------------------------------- 1 | interface DesktopMarqueeProps { 2 | children: React.ReactNode 3 | } 4 | 5 | const DesktopMarquee = ({ children }: DesktopMarqueeProps) => { 6 | return ( 7 |
8 |
9 | {/* 첫 번째 세트 */} 10 |
{children}
11 | {/* 두 번째 세트 (무한 루프를 위해 복제) */} 12 |
{children}
13 | {/* 세 번째 세트 (끝부분이 잘리지 않도록 추가) */} 14 |
{children}
15 |
16 |
17 | ) 18 | } 19 | 20 | export default DesktopMarquee 21 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Marquee/MarqueeWrapper.tsx: -------------------------------------------------------------------------------- 1 | import DesktopMarquee from './DesktopMarquee' 2 | import TabletMarquee from './TabletMarquee' 3 | import MobileMarquee from './MobileMarquee' 4 | 5 | interface MarqueeWrapperProps { 6 | children: React.ReactNode 7 | } 8 | 9 | const MarqueeWrapper = ({ children }: MarqueeWrapperProps) => { 10 | return ( 11 | <> 12 | {children} 13 | {children} 14 | {children} 15 | 16 | ) 17 | } 18 | 19 | export default MarqueeWrapper 20 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Marquee/MobileMarquee.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Children, useEffect, useRef } from 'react' 4 | 5 | interface MobileMarqueeProps { 6 | children: React.ReactNode 7 | } 8 | 9 | const MobileMarquee = ({ children }: MobileMarqueeProps) => { 10 | const topRowRef = useRef(null) 11 | const bottomRowRef = useRef(null) 12 | 13 | const childrenArray = Children.toArray(children) 14 | const halfLength = Math.ceil(childrenArray.length / 2) 15 | const topHalf = childrenArray.slice(0, halfLength) 16 | const bottomHalf = childrenArray.slice(halfLength) 17 | 18 | useEffect(() => { 19 | if (bottomRowRef.current) { 20 | bottomRowRef.current.scrollLeft = 114 21 | } 22 | }, []) 23 | 24 | return ( 25 |
26 | {/* 위층: 모바일 터치 스크롤 */} 27 |
35 |
36 |
{topHalf}
37 |
38 |
39 | 40 | {/* 아래층: 모바일 터치 스크롤 */} 41 |
49 |
50 |
{bottomHalf}
51 |
52 |
53 |
54 | ) 55 | } 56 | 57 | export default MobileMarquee 58 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Marquee/TabletMarquee.tsx: -------------------------------------------------------------------------------- 1 | import { Children } from 'react' 2 | 3 | interface TabletMarqueeProps { 4 | children: React.ReactNode 5 | } 6 | 7 | const TabletMarquee = ({ children }: TabletMarqueeProps) => { 8 | const childrenArray = Children.toArray(children) 9 | const halfLength = Math.ceil(childrenArray.length / 2) 10 | const topHalf = childrenArray.slice(0, halfLength) 11 | const bottomHalf = childrenArray.slice(halfLength) 12 | 13 | return ( 14 |
15 | {/* 위층: 태블릿 자동 애니메이션 */} 16 |
17 |
18 |
19 | {topHalf} 20 |
21 |
22 | {topHalf} 23 |
24 |
25 | {topHalf} 26 |
27 |
28 |
29 | 30 | {/* 아래층: 태블릿 자동 애니메이션 */} 31 |
32 |
33 |
34 | {bottomHalf} 35 |
36 |
37 | {bottomHalf} 38 |
39 |
40 | {bottomHalf} 41 |
42 |
43 |
44 |
45 | ) 46 | } 47 | 48 | export default TabletMarquee 49 | -------------------------------------------------------------------------------- /components/2025-commingsoon/shared/Typography/ScrambleText.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useRef } from 'react' 2 | 3 | interface ScrambleTextProps { 4 | text: string 5 | scrambleChars?: string 6 | duration?: number 7 | interval?: number 8 | keepChars?: string[] 9 | className?: string 10 | } 11 | 12 | const ScrambleText = ({ 13 | text, 14 | scrambleChars = '!@#$%^&*()_+-=[]{}|;:,.<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 15 | duration = 40, 16 | interval = 30, 17 | keepChars = [' ', '<', '>', '/', '.'], 18 | className = '', 19 | }: ScrambleTextProps) => { 20 | const [scrambledText, setScrambledText] = useState('') 21 | const [isScrambling, setIsScrambling] = useState(true) 22 | const intervalRef = useRef(null) 23 | 24 | useEffect(() => { 25 | let iterations = 0 26 | const maxIterations = duration 27 | 28 | const scramble = () => { 29 | setScrambledText( 30 | text 31 | .split('') 32 | .map((char, index) => { 33 | // 특정 문자는 그대로 유지 34 | if (keepChars.includes(char)) { 35 | return char 36 | } 37 | // 각 문자를 순차적으로 복원 38 | const revealTime = (index / text.length) * maxIterations 39 | if (iterations >= revealTime) { 40 | return char 41 | } 42 | return scrambleChars[ 43 | Math.floor(Math.random() * scrambleChars.length) 44 | ] 45 | }) 46 | .join('') 47 | ) 48 | 49 | iterations += 1 50 | 51 | if (iterations >= maxIterations) { 52 | setIsScrambling(false) 53 | setScrambledText(text) 54 | if (intervalRef.current) { 55 | clearInterval(intervalRef.current) 56 | intervalRef.current = null 57 | } 58 | } 59 | } 60 | 61 | // 초기 스크램블 시작 62 | intervalRef.current = setInterval(scramble, interval) 63 | 64 | return () => { 65 | if (intervalRef.current) { 66 | clearInterval(intervalRef.current) 67 | intervalRef.current = null 68 | } 69 | } 70 | // eslint-disable-next-line react-hooks/exhaustive-deps 71 | }, []) 72 | 73 | return ( 74 | 75 | {isScrambling ? scrambledText || text : text} 76 | 77 | ) 78 | } 79 | 80 | export default ScrambleText 81 | -------------------------------------------------------------------------------- /components/first/CodeOfConduct.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import FAQField from './common/FAQField' 4 | import { CodeOfConductData } from '@/data/first/FAQ' 5 | import type { FAQ as FAQType } from '@/types' 6 | 7 | const CodeOfConduct = () => { 8 | return ( 9 |
10 |
11 | {' '} 12 |
13 |

14 | 행동강령 15 |

16 |
17 |
18 | {CodeOfConductData.map((faq: FAQType) => ( 19 | 24 | ))} 25 |
26 |
27 |
28 | ) 29 | } 30 | 31 | export default CodeOfConduct 32 | -------------------------------------------------------------------------------- /components/first/FAQ.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import clsx from 'clsx' 3 | 4 | import FAQField from '@/components/first/common/FAQField' 5 | import { faqData } from '@/data/first/FAQ' 6 | import type { FAQ as FAQType } from '@/types' 7 | 8 | enum Category { 9 | APPLY = 'APPLY', 10 | ON_SITE = 'ON_SITE', 11 | EVENT = 'EVENT', 12 | ETC = 'ETC', 13 | } 14 | 15 | const FAQ = () => { 16 | const [selected, setSelected] = useState(Category.APPLY) 17 | 18 | const onClickCategory = (category: Category) => { 19 | setSelected(category) 20 | } 21 | 22 | return ( 23 |
24 |
25 |
26 |

27 | 자주 묻는 질문 28 |

29 |
30 | 41 | 52 | 63 | 74 |
75 |
76 |
77 | {faqData[selected].map((faq: FAQType) => ( 78 | 83 | ))} 84 |
85 |
86 |
87 | ) 88 | } 89 | 90 | export default FAQ 91 | -------------------------------------------------------------------------------- /components/first/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | 3 | import Logo from '@/public/images/Logo_1.png' 4 | import Instagram from '@/public/images/Instagram.svg' 5 | import LinkedIn from '@/public/images/LinkedIn.svg' 6 | import Twitter from '@/public/images/Twitter.svg' 7 | import UpArrow from '@/public/images/UpArrow.svg' 8 | import { useState } from 'react' 9 | import PrivacyModal from './PrivacyModal' 10 | 11 | const Footer = () => { 12 | const [isModalOpen, setIsModalOpen] = useState(false) 13 | 14 | const powerModal = () => { 15 | setIsModalOpen((prev) => !prev) 16 | } 17 | 18 | return ( 19 |
20 |
21 | logo 22 |

23 | ⓒ TEOCON. All Right Reserved. 24 |

25 |
26 | 33 | 62 |
63 |
64 | 74 | {isModalOpen && } 75 |
76 | ) 77 | } 78 | 79 | export default Footer 80 | -------------------------------------------------------------------------------- /components/first/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Hero = () => ( 4 |
5 |
6 |

TEOConf

7 |
8 |

테오의 컨퍼런스

9 |

10 | 2023.5.20. 토요일, 신촌역 사람인 카페 11 |

12 |
13 |
14 |
15 | ) 16 | 17 | export default Hero 18 | -------------------------------------------------------------------------------- /components/first/Map.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import KakaoMap from '@/components/first/common/KakaoMap' 4 | 5 | const Map = () => { 6 | return ( 7 |
11 |
12 |
13 |
14 |
15 | 5월 20일
16 | 여기서 만나요! 17 |
18 |

19 | 신촌역 3번출구에서 300M 내에{' '} 20 |
위치하고 있습니다! 21 |

22 |
23 | 27 |
28 |
29 |
30 | ) 31 | } 32 | 33 | export default Map 34 | -------------------------------------------------------------------------------- /components/first/NameCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NameCard = () => ( 4 |
8 |
9 |
10 |
11 |
12 |

13 | 명함 굿즈 14 |

15 |

16 | 참가자 전원에게 명함 굿즈를 제공합니다. 17 |

18 |
19 |
20 |
21 |
22 |
23 |
24 | ) 25 | 26 | export default NameCard 27 | -------------------------------------------------------------------------------- /components/first/SNS.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | 4 | import LinkedIn from '@/public/images/LinkedIn.svg' 5 | import Twitter from '@/public/images/Twitter.svg' 6 | import Instagram from '@/public/images/Instagram.svg' 7 | 8 | const SNS = () => ( 9 |
10 |
11 |

12 | 테오콘 소식을
더 빨리 알고 싶다면 13 |

14 | 55 |
56 |
57 | ) 58 | 59 | export default SNS 60 | -------------------------------------------------------------------------------- /components/first/Sponsor.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | import JumpitLogo from '@/public/images/JumpitLogo.svg' 4 | 5 | const Sponsor = () => ( 6 | 58 | ) 59 | 60 | export default Sponsor 61 | -------------------------------------------------------------------------------- /components/first/common/FAQField.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import clsx from 'clsx' 3 | import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown' 4 | import KeyboardArrowUp from '@mui/icons-material/KeyboardArrowUp' 5 | 6 | interface FAQFieldProps { 7 | question: string 8 | answer: string 9 | } 10 | 11 | const FAQField = ({ question, answer }: FAQFieldProps) => { 12 | const [isOpen, setIsOpen] = useState(false) 13 | 14 | const onClickFAQ = () => { 15 | setIsOpen(!isOpen) 16 | } 17 | 18 | return ( 19 |
20 |
27 |

28 | {question} 29 |

30 | {isOpen ? : } 31 |
32 |
38 |

39 | {answer} 40 |

41 |
42 |
43 | ) 44 | } 45 | 46 | export default FAQField 47 | -------------------------------------------------------------------------------- /components/first/common/KakaoMap.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import LocationOnIcon from '@mui/icons-material/LocationOn' 3 | 4 | interface KakaoMapProps { 5 | latitude: number 6 | longitude: number 7 | } 8 | 9 | const KakaoMap = ({ latitude, longitude }: KakaoMapProps) => { 10 | useEffect(() => { 11 | const mapScript = document.createElement('script') 12 | 13 | mapScript.async = true 14 | mapScript.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_MAP}&autoload=false` 15 | 16 | document.head.appendChild(mapScript) 17 | 18 | const onLoadKakaoMap = () => { 19 | window.kakao.maps.load(() => { 20 | const container = document.getElementById('map') 21 | const options = { 22 | center: new window.kakao.maps.LatLng(latitude, longitude), 23 | } 24 | const map = new window.kakao.maps.Map(container, options) 25 | const markerPosition = new window.kakao.maps.LatLng(latitude, longitude) 26 | const marker = new window.kakao.maps.Marker({ 27 | position: markerPosition, 28 | }) 29 | marker.setMap(map) 30 | }) 31 | } 32 | mapScript.addEventListener('load', onLoadKakaoMap) 33 | 34 | return () => mapScript.removeEventListener('load', onLoadKakaoMap) 35 | }, [latitude, longitude]) 36 | 37 | return ( 38 |
39 | 52 | ) 53 | } 54 | 55 | export default KakaoMap 56 | -------------------------------------------------------------------------------- /components/first/common/StaffCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image, { StaticImageData } from 'next/image' 3 | import clsx from 'clsx' 4 | 5 | import GithubLogo from '@/public/images/GithubLogo.svg' 6 | import Behance from '@/public/images/Behance.svg' 7 | 8 | interface StaffCardProps { 9 | name: string 10 | description: string 11 | image: StaticImageData 12 | url: string 13 | position: 'developer' | 'designer' 14 | className?: string 15 | } 16 | 17 | const StaffCard = ({ 18 | name, 19 | description, 20 | image, 21 | url, 22 | position, 23 | className, 24 | }: StaffCardProps) => { 25 | return ( 26 |
  • 32 | {`${name} 33 | 38 |
    39 |

    40 | {name} 41 |

    42 |
    43 | {position === 'developer' ? ( 44 | github 바로가기 49 | ) : ( 50 | behance 바로가기 55 | )} 56 |
    57 |
    58 |

    59 | {description} 60 |

    61 |
    62 |
  • 63 | ) 64 | } 65 | 66 | export default StaffCard 67 | -------------------------------------------------------------------------------- /components/first/common/SwiperSlide.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import clsx from 'clsx' 3 | 4 | interface SwiperSlideProps { 5 | children: React.ReactNode 6 | direction?: 'left' | 'right' 7 | className?: string 8 | } 9 | 10 | const SwiperSlide = ({ 11 | children, 12 | direction = 'left', 13 | className, 14 | }: SwiperSlideProps) => { 15 | return ( 16 |
      22 | {children} 23 |
    24 | ) 25 | } 26 | 27 | export default SwiperSlide 28 | -------------------------------------------------------------------------------- /components/first/gallery/NavButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSwiper } from 'swiper/react' 3 | import Image from 'next/image' 4 | 5 | import ArrowLeft from '@/public/images/ArrowLeft.svg' 6 | import ArrowRight from '@/public/images/ArrowRight.svg' 7 | import clsx from 'clsx' 8 | 9 | interface MobileNavButtonsProps { 10 | className?: string 11 | } 12 | 13 | const MobileNavButton = ({ className }: MobileNavButtonsProps) => { 14 | const swiper = useSwiper() 15 | 16 | return ( 17 |
    18 | 24 | 30 |
    31 | ) 32 | } 33 | 34 | export default MobileNavButton 35 | -------------------------------------------------------------------------------- /components/first/introduce/Talk.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import React from 'react' 3 | 4 | interface TalkProps { 5 | contents: string 6 | speaker: string 7 | Icon: React.ReactNode 8 | classNames?: string 9 | } 10 | 11 | const Talk = ({ contents, speaker, Icon, classNames }: TalkProps) => { 12 | return ( 13 |
  • 14 |
    15 |

    16 | {contents} 17 |

    18 |

    19 | {speaker} 20 |

    21 |
    22 |
    23 | {Icon} 24 |
  • 25 | ) 26 | } 27 | 28 | export default Talk 29 | -------------------------------------------------------------------------------- /components/second/Banner.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | import KeyboardDoubleArrowDownOutlinedIcon from '@mui/icons-material/KeyboardDoubleArrowDownOutlined' 4 | 5 | import Mouse from '@/public/images/Mouse_2.svg' 6 | import TabletBI1 from '@/public/images/TabletBI1_2.svg' 7 | import TabletBI2 from '@/public/images/TabletBI2_2.svg' 8 | import Yellow from '@/public/images/yellow_2.svg' 9 | import Red from '@/public/images/red_2.svg' 10 | 11 | const Banner = () => { 12 | return ( 13 | 53 | ) 54 | } 55 | 56 | export default Banner 57 | -------------------------------------------------------------------------------- /components/second/CodeOfConduct.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import FAQField from './common/FAQField' 4 | import { CodeOfConductData } from '@/data/second/FAQ' 5 | import type { FAQ as FAQType } from '@/types' 6 | 7 | const CodeOfConduct = () => { 8 | return ( 9 |
    10 |
    11 | {' '} 12 |
    13 |

    14 | 행동강령 15 |

    16 |
    17 |
    18 | {CodeOfConductData.map((faq: FAQType) => ( 19 | 24 | ))} 25 |
    26 |
    27 |
    28 | ) 29 | } 30 | 31 | export default CodeOfConduct 32 | -------------------------------------------------------------------------------- /components/second/FAQ.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import clsx from 'clsx' 3 | 4 | import FAQField from '@/components/second/common/FAQField' 5 | import { faqData } from '@/data/second/FAQ' 6 | import type { FAQ as FAQType } from '@/types' 7 | 8 | enum Category { 9 | APPLY = 'APPLY', 10 | ON_SITE = 'ON_SITE', 11 | EVENT = 'EVENT', 12 | ETC = 'ETC', 13 | } 14 | 15 | const FAQ = () => { 16 | const [selected, setSelected] = useState(Category.APPLY) 17 | 18 | const onClickCategory = (category: Category) => { 19 | setSelected(category) 20 | } 21 | 22 | return ( 23 |
    24 |
    25 |
    26 |

    27 | 자주 묻는 질문 28 |

    29 |
    30 | 41 | 52 | 63 | 74 |
    75 |
    76 |
    77 | {faqData[selected].map((faq: FAQType) => ( 78 | 83 | ))} 84 |
    85 |
    86 |
    87 | ) 88 | } 89 | 90 | export default FAQ 91 | -------------------------------------------------------------------------------- /components/second/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | 3 | import Logo from '@/public/images/Logo_2.svg' 4 | import Instagram from '@/public/images/Instagram_2.svg' 5 | import LinkedIn from '@/public/images/LinkedIn_2.svg' 6 | import Twitter from '@/public/images/Twitter_2.svg' 7 | import UpArrow from '@/public/images/UpArrow_2.svg' 8 | import { useState } from 'react' 9 | import PrivacyModal from './PrivacyModal' 10 | 11 | const Footer = () => { 12 | const [isModalOpen, setIsModalOpen] = useState(false) 13 | 14 | const powerModal = () => { 15 | setIsModalOpen((prev) => !prev) 16 | } 17 | 18 | return ( 19 |
    20 |
    21 | logo 22 |

    23 | ⓒ TEOCON. All Right Reserved. 24 |

    25 |
    26 | 33 | 62 |
    63 |
    64 | 74 | {isModalOpen && } 75 |
    76 | ) 77 | } 78 | 79 | export default Footer 80 | -------------------------------------------------------------------------------- /components/second/Header.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import clsx from 'clsx' 3 | import Image from 'next/image' 4 | import { useRouter } from 'next/router' 5 | import CloseIcon from '@mui/icons-material/Close' 6 | import MenuIcon from '@mui/icons-material/Menu' 7 | import LaunchIcon from '@mui/icons-material/Launch' 8 | 9 | import useIntersectionObservation from '@/lib/observer' 10 | import Logo from '@/public/images/Logo_2.svg' 11 | import Sidebar from './Sidebar' 12 | import { SectionTab } from './common/SectionTab' 13 | 14 | export const Header = () => { 15 | const router = useRouter() 16 | 17 | const [currentId, setCurrentId] = useState('banner') 18 | const [isOpen, setIsOpen] = useState(false) 19 | 20 | useIntersectionObservation(setCurrentId, currentId) 21 | 22 | const toggleSide = () => { 23 | setIsOpen((prev) => !prev) 24 | } 25 | 26 | return ( 27 |
    32 | 81 |
    82 | ) 83 | } 84 | 85 | export default Header 86 | -------------------------------------------------------------------------------- /components/second/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Hero = () => ( 4 |
    5 |
    6 |

    TEOConf

    7 |
    8 |

    테오의 컨퍼런스

    9 |

    10 | 2023.5.20. 토요일, 신촌역 사람인 카페 11 |

    12 |
    13 |
    14 |
    15 | ) 16 | 17 | export default Hero 18 | -------------------------------------------------------------------------------- /components/second/Map.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import KakaoMap from '@/components/second/common/KakaoMap' 4 | 5 | const Map = () => { 6 | return ( 7 |
    11 |
    12 |
    13 |
    14 |
    15 | 10월 28일
    16 | 여기서 만나요! 17 |
    18 |

    19 | 구로 디지털 단지역 3번출구에서 200M 내에{' '} 20 |
    위치하고 있습니다! 21 |

    22 |
    23 | 27 |
    28 |
    29 |
    30 | ) 31 | } 32 | 33 | export default Map 34 | -------------------------------------------------------------------------------- /components/second/NameCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NameCard = () => ( 4 |
    8 |
    9 |
    10 |
    11 |
    12 |

    13 | 명함 굿즈 14 |

    15 |

    16 | 참가자 전원에게 명함 굿즈를 제공합니다. 17 |

    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 | ) 25 | 26 | export default NameCard 27 | -------------------------------------------------------------------------------- /components/second/SNS.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | 4 | import LinkedIn from '@/public/images/LinkedIn.svg' 5 | import Twitter from '@/public/images/Twitter.svg' 6 | import Instagram from '@/public/images/Instagram.svg' 7 | 8 | const SNS = () => ( 9 |
    10 |
    11 |

    12 | 테오콘 소식을
    더 빨리 알고 싶다면 13 |

    14 | 55 |
    56 |
    57 | ) 58 | 59 | export default SNS 60 | -------------------------------------------------------------------------------- /components/second/Sponsor.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | import JumpitLogo from '@/public/images/JumpitLogo.svg' 4 | 5 | const Sponsor = () => ( 6 | 55 | ) 56 | 57 | export default Sponsor 58 | -------------------------------------------------------------------------------- /components/second/common/FAQField.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import clsx from 'clsx' 3 | import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown' 4 | import KeyboardArrowUp from '@mui/icons-material/KeyboardArrowUp' 5 | 6 | interface FAQFieldProps { 7 | question: string 8 | answer: string 9 | } 10 | 11 | const FAQField = ({ question, answer }: FAQFieldProps) => { 12 | const [isOpen, setIsOpen] = useState(false) 13 | 14 | const onClickFAQ = () => { 15 | setIsOpen(!isOpen) 16 | } 17 | 18 | return ( 19 |
    20 |
    27 |

    28 | {question} 29 |

    30 | {isOpen ? : } 31 |
    32 |
    38 |

    39 | {answer} 40 |

    41 |
    42 |
    43 | ) 44 | } 45 | 46 | export default FAQField 47 | -------------------------------------------------------------------------------- /components/second/common/KakaoMap.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import LocationOnIcon from '@mui/icons-material/LocationOn' 3 | 4 | interface KakaoMapProps { 5 | latitude: number 6 | longitude: number 7 | } 8 | 9 | const KakaoMap = ({ latitude, longitude }: KakaoMapProps) => { 10 | useEffect(() => { 11 | const mapScript = document.createElement('script') 12 | 13 | mapScript.async = true 14 | mapScript.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_MAP}&autoload=false` 15 | 16 | document.head.appendChild(mapScript) 17 | 18 | const onLoadKakaoMap = () => { 19 | window.kakao.maps.load(() => { 20 | const container = document.getElementById('map') 21 | const options = { 22 | center: new window.kakao.maps.LatLng(latitude, longitude), 23 | } 24 | const map = new window.kakao.maps.Map(container, options) 25 | const markerPosition = new window.kakao.maps.LatLng(latitude, longitude) 26 | const marker = new window.kakao.maps.Marker({ 27 | position: markerPosition, 28 | }) 29 | marker.setMap(map) 30 | }) 31 | } 32 | mapScript.addEventListener('load', onLoadKakaoMap) 33 | 34 | return () => mapScript.removeEventListener('load', onLoadKakaoMap) 35 | }, [latitude, longitude]) 36 | 37 | return ( 38 |
    39 | 52 | ) 53 | } 54 | 55 | export default KakaoMap 56 | -------------------------------------------------------------------------------- /components/second/common/SectionTab.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import type { PropsWithChildren } from 'react' 3 | 4 | interface SectionTabProps { 5 | section: string 6 | currentSection: string 7 | } 8 | 9 | export const SectionTab = ({ 10 | children, 11 | section, 12 | currentSection, 13 | }: PropsWithChildren) => { 14 | return ( 15 | 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /components/second/common/StaffCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image, { StaticImageData } from 'next/image' 3 | import clsx from 'clsx' 4 | 5 | import GithubLogo from '@/public/images/GithubLogoPurple.svg' 6 | import Behance from '@/public/images/BehancePurple.svg' 7 | 8 | interface StaffCardProps { 9 | name: string 10 | description: string 11 | image: StaticImageData 12 | url: string 13 | position: 'developer' | 'designer' 14 | className?: string 15 | } 16 | 17 | const StaffCard = ({ 18 | name, 19 | description, 20 | image, 21 | url, 22 | position, 23 | className, 24 | }: StaffCardProps) => { 25 | return ( 26 |
  • 32 | {`${name} 37 | 42 |
    43 |

    44 | {name} 45 |

    46 |
    47 | {position === 'developer' ? ( 48 | github 바로가기 53 | ) : ( 54 | behance 바로가기 59 | )} 60 |
    61 |
    62 |

    63 | {description} 64 |

    65 |
    66 |
  • 67 | ) 68 | } 69 | 70 | export default StaffCard 71 | -------------------------------------------------------------------------------- /components/second/common/SwiperSlide.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import clsx from 'clsx' 3 | 4 | interface SwiperSlideProps { 5 | children: React.ReactNode 6 | direction?: 'left' | 'right' 7 | className?: string 8 | } 9 | 10 | const SwiperSlide = ({ 11 | children, 12 | direction = 'left', 13 | className, 14 | }: SwiperSlideProps) => { 15 | return ( 16 |
      22 | {children} 23 |
    24 | ) 25 | } 26 | 27 | export default SwiperSlide 28 | -------------------------------------------------------------------------------- /components/second/gallery/NavButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSwiper } from 'swiper/react' 3 | import Image from 'next/image' 4 | 5 | import ArrowLeft from '@/public/images/ArrowLeft.svg' 6 | import ArrowRight from '@/public/images/ArrowRight.svg' 7 | import clsx from 'clsx' 8 | 9 | interface MobileNavButtonsProps { 10 | className?: string 11 | } 12 | 13 | const MobileNavButton = ({ className }: MobileNavButtonsProps) => { 14 | const swiper = useSwiper() 15 | 16 | return ( 17 |
    18 | 24 | 30 |
    31 | ) 32 | } 33 | 34 | export default MobileNavButton 35 | -------------------------------------------------------------------------------- /components/second/introduce/Talk.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import clsx from 'clsx' 3 | 4 | interface TalkProps { 5 | contents: string 6 | speaker: string 7 | Icon: React.ReactNode 8 | classNames?: string 9 | } 10 | 11 | const Talk = ({ contents, speaker, Icon, classNames }: TalkProps) => { 12 | return ( 13 |
  • 14 |
    15 |

    16 | {contents} 17 |

    18 |

    19 | {speaker} 20 |

    21 |
    22 |
    23 | {Icon} 24 |
  • 25 | ) 26 | } 27 | 28 | export default Talk 29 | -------------------------------------------------------------------------------- /components/third/CodeOfConduct.tsx: -------------------------------------------------------------------------------- 1 | import { CodeOfConductData } from '@/data/third/FAQ' 2 | import type { FAQ as FAQType } from '@/types' 3 | import FAQField from './common/FAQField' 4 | 5 | const CodeOfConduct = () => { 6 | return ( 7 |
    8 |
    9 |
    10 |

    11 | 우리들의 약속
    12 | (Ground Rule) 13 |

    14 |
    15 |
    16 | {CodeOfConductData.map((faq: FAQType) => ( 17 | 22 | ))} 23 |
    24 |
    25 |
    26 | ) 27 | } 28 | 29 | export default CodeOfConduct 30 | -------------------------------------------------------------------------------- /components/third/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | 3 | import { links } from '@/data/third/Links' 4 | import Instagram from '@/public/images/Instagram_2.svg' 5 | import LinkedIn from '@/public/images/LinkedIn_2.svg' 6 | import Logo from '@/public/images/Logo_3.svg' 7 | import Twitter from '@/public/images/Twitter_2.svg' 8 | import UpArrow from '@/public/images/UpArrow_3.svg' 9 | import { useState } from 'react' 10 | import PrivacyModal from './PrivacyModal' 11 | 12 | const Footer = () => { 13 | const [isModalOpen, setIsModalOpen] = useState(false) 14 | 15 | const powerModal = () => { 16 | setIsModalOpen((prev) => !prev) 17 | } 18 | 19 | return ( 20 |
    21 |
    22 | logo 23 |

    24 | ⓒ TEOCON. All Right Reserved. 25 |

    26 |
    27 | 34 | 60 |
    61 |
    62 | 72 | {isModalOpen && } 73 |
    74 | ) 75 | } 76 | 77 | export default Footer 78 | -------------------------------------------------------------------------------- /components/third/Header/PassedConfMenu.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '@mui/material' 2 | import ClickAwayListener from '@mui/material/ClickAwayListener' 3 | import Grow from '@mui/material/Grow' 4 | import MenuItem from '@mui/material/MenuItem' 5 | import MenuList from '@mui/material/MenuList' 6 | import Paper from '@mui/material/Paper' 7 | import Popper from '@mui/material/Popper' 8 | import Stack from '@mui/material/Stack' 9 | import { useRouter } from 'next/router' 10 | import { SyntheticEvent, useEffect, useRef, useState } from 'react' 11 | 12 | const PassedConfMenu = () => { 13 | const [open, setOpen] = useState(false) 14 | const anchorRef = useRef(null) 15 | const router = useRouter() 16 | 17 | const handleRedirect = (path: string) => { 18 | return (event: Event | SyntheticEvent) => { 19 | router.push(path) 20 | handleClose(event) 21 | } 22 | } 23 | 24 | const handleToggle = () => { 25 | setOpen((prevOpen) => !prevOpen) 26 | } 27 | 28 | const handleClose = (event: Event | SyntheticEvent) => { 29 | if ( 30 | anchorRef.current && 31 | anchorRef.current.contains(event.target as HTMLElement) 32 | ) { 33 | return 34 | } 35 | 36 | setOpen(false) 37 | } 38 | 39 | function handleListKeyDown(event: KeyboardEvent) { 40 | if (event.key === 'Tab') { 41 | event.preventDefault() 42 | setOpen(false) 43 | } else if (event.key === 'Escape') { 44 | setOpen(false) 45 | } 46 | } 47 | 48 | const prevOpen = useRef(open) 49 | useEffect(() => { 50 | if (prevOpen.current === true && open === false) { 51 | anchorRef.current!.focus() 52 | } 53 | 54 | prevOpen.current = open 55 | }, [open]) 56 | 57 | return ( 58 | 59 | 64 | 지난 행사 65 | 66 | 74 | {({ TransitionProps, placement }) => ( 75 | 82 | 83 | 84 | 85 | 86 | 테오콘 1기 87 | 88 | 89 | 테오콘 2기 90 | 91 | 92 | 93 | 94 | 95 | )} 96 | 97 | 98 | ) 99 | } 100 | 101 | export default PassedConfMenu 102 | -------------------------------------------------------------------------------- /components/third/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Hero = () => ( 4 |
    5 |
    6 |

    TEOConf

    7 |
    8 |

    테오의 컨퍼런스

    9 |

    10 | 2023.5.20. 토요일, 신촌역 사람인 카페 11 |

    12 |
    13 |
    14 |
    15 | ) 16 | 17 | export default Hero 18 | -------------------------------------------------------------------------------- /components/third/Map.tsx: -------------------------------------------------------------------------------- 1 | import KakaoMap from '@/components/third/common/KakaoMap' 2 | 3 | const Map = () => { 4 | return ( 5 |
    9 |
    10 |
    11 |
    12 |
    13 | 테오의 컨퍼런스,
    14 | 여기서 만나요! 15 |
    16 |
    17 |

    18 | 서울특별시 강남구 테헤란로 44길 8{' '} 19 |
    20 | (아이콘역삼빌딩, 12층) 21 |

    22 |

    23 | 선릉역 4번 출구에서 400m 24 |

    25 |
    26 |
    27 | 31 |
    32 |
    33 |
    34 | ) 35 | } 36 | 37 | export default Map 38 | -------------------------------------------------------------------------------- /components/third/NameCard.tsx: -------------------------------------------------------------------------------- 1 | const NameCard = () => ( 2 |
    3 |
    4 |
    5 |
    6 |
    7 |

    8 | 참가자 전원에게
    9 | 명함 굿즈를 10 | 제공합니다. 11 |

    12 |

    13 | 명함을 건네며 자연스럽게 네트워킹해 보아요. 14 |

    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 | ) 22 | 23 | export default NameCard 24 | -------------------------------------------------------------------------------- /components/third/Program.tsx: -------------------------------------------------------------------------------- 1 | import NameCard from './NameCard' 2 | import Networking from './NetWorking' 3 | import Session from './Session' 4 | 5 | const Program = () => { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ) 12 | } 13 | 14 | export default Program 15 | -------------------------------------------------------------------------------- /components/third/SideBar/SideBarItem.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { useMemo, useState } from 'react' 3 | import { Menu } from '@/data/third/Menu' 4 | import { KeyboardArrowUp } from '@mui/icons-material' 5 | import clsx from 'clsx' 6 | 7 | interface Props { 8 | menu: Menu 9 | currentId: string 10 | } 11 | 12 | const SideBarItem = ({ menu, currentId }: Props) => { 13 | const selected = useMemo(() => menu.id === currentId, [currentId]) 14 | const hasChildren = useMemo(() => typeof menu.children !== 'undefined', []) 15 | const link = useMemo(() => menu.link ?? `#${menu.id}`, []) 16 | 17 | const [isOpen, setIsOpen] = useState(false) 18 | const toggleIsOpen = () => setIsOpen((prev) => !prev) 19 | 20 | return ( 21 | <> 22 |
    29 | 30 | {menu.name} 31 | 32 | {hasChildren && ( 33 |
    34 | 42 |
    43 | )} 44 |
    45 | {menu.children && ( 46 |
    52 | {menu.children.map((item, idx) => { 53 | return ( 54 |
    55 | {item.name} 56 |
    57 | ) 58 | })} 59 |
    60 | )} 61 | 62 | ) 63 | } 64 | 65 | export default SideBarItem 66 | -------------------------------------------------------------------------------- /components/third/SideBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { links } from '@/data/third/Links' 2 | import { menu } from '@/data/third/Menu' 3 | import LaunchIcon from '@mui/icons-material/Launch' 4 | import clsx from 'clsx' 5 | import { useEffect, useRef } from 'react' 6 | import SideBarItem from './SideBarItem' 7 | 8 | interface Props { 9 | currentId: string 10 | isOpen: boolean 11 | close: () => void 12 | } 13 | 14 | const SideBar = ({ currentId, isOpen, close }: Props) => { 15 | const mount = useRef(false) 16 | 17 | // herf action to close sidebar 18 | useEffect(() => { 19 | if (mount) { 20 | document.querySelectorAll("a[href^='#']").forEach((anchor) => { 21 | anchor.addEventListener('click', close) 22 | }) 23 | } 24 | return () => { 25 | document.querySelectorAll("a[href^='#']").forEach((anchor) => { 26 | anchor.removeEventListener('click', close) 27 | }) 28 | } 29 | }, [isOpen, close]) 30 | 31 | useEffect(() => { 32 | mount.current = true 33 | }, []) 34 | 35 | return ( 36 |
    42 |
    43 | {menu.map((item) => ( 44 | 49 | ))} 50 | 60 |
    61 |
    62 | ) 63 | } 64 | 65 | export default SideBar 66 | -------------------------------------------------------------------------------- /components/third/Sponsor.tsx: -------------------------------------------------------------------------------- 1 | import { eventDetails } from '@/data/third/EventDetails' 2 | import { sponsor } from '@/data/third/Sponsor' 3 | import HHPlusLogo from '@/public/images/HHPlusLogo.svg' 4 | import Image from 'next/image' 5 | 6 | const Sponsor = () => ( 7 |
    8 |
    9 |
    10 |
    11 |

    12 | {eventDetails.name_ko}는
    13 | 14 | 현직 개발자 역량 강화 코스 15 |
    16 | {sponsor.name} 17 |
    18 | 와 함께합니다. 19 |
    20 |

    21 |

    22 | {eventDetails.name_ko}는{' '} 23 | 24 | 현직 개발자 역량 강화 코스{' '} 25 | {sponsor.name} 26 | 27 | 와 함께합니다. 28 |
    29 |

    30 |
    31 |
    32 |
    33 | 34 | HHPlusLogo 41 | 42 |
    43 |
    44 |
    45 | ) 46 | 47 | export default Sponsor 48 | -------------------------------------------------------------------------------- /components/third/Timetable.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import { timetables } from '@/data/third/Timetable' 3 | 4 | const Timetable = () => { 5 | return ( 6 |
    10 |
    11 |

    타임테이블

    12 |
    13 | {timetables.map((timetable) => ( 14 |
    18 |

    19 | {timetable.date} 20 |

    21 |
      22 | {timetable.tables.map((table) => ( 23 |
    • 27 |
      28 | {table.startTime} 29 | 30 | {table.endTime} 31 |
      32 |
      40 | {table.content} 41 |
      42 |
    • 43 | ))} 44 |
    45 |
    46 | ))} 47 |
    48 |
    49 |
    50 | ) 51 | } 52 | 53 | export default Timetable 54 | -------------------------------------------------------------------------------- /components/third/common/FAQField.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import clsx from 'clsx' 3 | import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material' 4 | import { ExpandMore } from '@mui/icons-material' 5 | 6 | interface FAQFieldProps { 7 | question: string 8 | answer: string 9 | } 10 | 11 | const FAQField = ({ question, answer }: FAQFieldProps) => { 12 | const [isOpen, setIsOpen] = useState(false) 13 | 14 | const onClickFAQ = () => { 15 | setIsOpen(!isOpen) 16 | } 17 | 18 | return ( 19 | 23 | } 25 | className={clsx( 26 | 'flex justify-between w-full items-center px-4 py-3.5 tablet:py-5 tablet:px-8 transition-all min-h-fit [&_.MuiAccordionSummary-content]:m-0', 27 | isOpen ? 'rounded-t-lg' : 'rounded-lg' 28 | )} 29 | onClick={onClickFAQ} 30 | > 31 |

    {question}

    32 |
    33 | 39 |

    40 | {answer} 41 |

    42 |
    43 |
    44 | ) 45 | } 46 | 47 | export default FAQField 48 | -------------------------------------------------------------------------------- /components/third/common/KakaoMap.tsx: -------------------------------------------------------------------------------- 1 | import LocationOnIcon from '@mui/icons-material/LocationOn' 2 | import { useEffect } from 'react' 3 | 4 | interface KakaoMapProps { 5 | latitude: number 6 | longitude: number 7 | } 8 | 9 | const KakaoMap = ({ latitude, longitude }: KakaoMapProps) => { 10 | useEffect(() => { 11 | const mapScript = document.createElement('script') 12 | 13 | mapScript.async = true 14 | mapScript.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_MAP}&autoload=false` 15 | 16 | document.head.appendChild(mapScript) 17 | 18 | const onLoadKakaoMap = () => { 19 | window.kakao.maps.load(() => { 20 | const container = document.getElementById('map') 21 | const options = { 22 | center: new window.kakao.maps.LatLng(latitude, longitude), 23 | } 24 | const map = new window.kakao.maps.Map(container, options) 25 | const markerPosition = new window.kakao.maps.LatLng(latitude, longitude) 26 | const marker = new window.kakao.maps.Marker({ 27 | position: markerPosition, 28 | }) 29 | marker.setMap(map) 30 | }) 31 | } 32 | mapScript.addEventListener('load', onLoadKakaoMap) 33 | 34 | return () => mapScript.removeEventListener('load', onLoadKakaoMap) 35 | }, [latitude, longitude]) 36 | 37 | return ( 38 |
    39 | 52 | ) 53 | } 54 | 55 | export default KakaoMap 56 | -------------------------------------------------------------------------------- /components/third/common/SectionTab.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import type { PropsWithChildren } from 'react' 3 | 4 | interface SectionTabProps { 5 | section: string 6 | currentSection: string 7 | } 8 | 9 | export const SectionTab = ({ 10 | children, 11 | section, 12 | currentSection, 13 | }: PropsWithChildren) => { 14 | return ( 15 | 23 | {children} 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /components/third/common/StaffCard.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import Image, { StaticImageData } from 'next/image' 3 | 4 | import Behance from '@/public/images/BehancePurple.svg' 5 | import GithubLogo from '@/public/images/GithubLogoWhite.svg' 6 | 7 | interface StaffCardProps { 8 | name: string 9 | description: string 10 | image: StaticImageData 11 | url: string 12 | position: 'developer' | 'designer' 13 | className?: string 14 | } 15 | 16 | const StaffCard = ({ 17 | name, 18 | description, 19 | image, 20 | url, 21 | position, 22 | className, 23 | }: StaffCardProps) => { 24 | return ( 25 |
  • 31 | {`${name} 37 | 42 |
    43 |

    44 | {name} 45 |

    46 |
    47 | {position === 'developer' ? ( 48 | github 바로가기 53 | ) : ( 54 | behance 바로가기 59 | )} 60 |
    61 |
    62 |

    63 | {description} 64 |

    65 |
    66 |
  • 67 | ) 68 | } 69 | 70 | export default StaffCard 71 | -------------------------------------------------------------------------------- /components/third/common/SwiperSlide.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import clsx from 'clsx' 3 | 4 | interface SwiperSlideProps { 5 | children: React.ReactNode 6 | direction?: 'left' | 'right' 7 | className?: string 8 | } 9 | 10 | const SwiperSlide = ({ 11 | children, 12 | direction = 'left', 13 | className, 14 | }: SwiperSlideProps) => { 15 | return ( 16 |
      22 | {children} 23 |
    24 | ) 25 | } 26 | 27 | export default SwiperSlide 28 | -------------------------------------------------------------------------------- /components/third/gallery/NavButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSwiper } from 'swiper/react' 3 | import Image from 'next/image' 4 | 5 | import ArrowLeft from '@/public/images/ArrowLeft.svg' 6 | import ArrowRight from '@/public/images/ArrowRight.svg' 7 | import clsx from 'clsx' 8 | 9 | interface MobileNavButtonsProps { 10 | className?: string 11 | } 12 | 13 | const MobileNavButton = ({ className }: MobileNavButtonsProps) => { 14 | const swiper = useSwiper() 15 | 16 | return ( 17 |
    18 | 24 | 30 |
    31 | ) 32 | } 33 | 34 | export default MobileNavButton 35 | -------------------------------------------------------------------------------- /components/third/introduce/Talk.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import React from 'react' 3 | 4 | interface TalkProps { 5 | contents: string 6 | speaker: string 7 | Icon: React.ReactNode 8 | classNames?: string 9 | } 10 | 11 | const Talk = ({ contents, speaker, Icon, classNames }: TalkProps) => { 12 | return ( 13 |
  • 14 |
    15 |

    16 | {contents} 17 |

    18 |

    19 | {speaker} 20 |

    21 |
    22 |
    23 | {Icon} 24 |
  • 25 | ) 26 | } 27 | 28 | export default Talk 29 | -------------------------------------------------------------------------------- /data/2025/navigation.ts: -------------------------------------------------------------------------------- 1 | export interface ArchiveItem { 2 | name: string 3 | path: string 4 | } 5 | 6 | export interface SidebarItem { 7 | name: string 8 | sectionId: string 9 | } 10 | 11 | export const ARCHIVE_ITEMS: ArchiveItem[] = [ 12 | { name: '테오콘 1기', path: '/first' }, 13 | { name: '테오콘 2기', path: '/second' }, 14 | { name: '테오콘 2024', path: '/third' }, 15 | ] 16 | 17 | export const SIDEBAR_ITEMS: SidebarItem[] = [ 18 | { name: '모아보기', sectionId: 'overview' }, 19 | { name: '후원사', sectionId: 'sponsor' }, 20 | ] 21 | -------------------------------------------------------------------------------- /data/first/FAQ.ts: -------------------------------------------------------------------------------- 1 | import { FAQ as FAQType } from '@/types' 2 | 3 | interface FAQData { 4 | [key: string]: FAQType[] 5 | } 6 | 7 | export const faqData: FAQData = { 8 | APPLY: [ 9 | { 10 | question: '등록이 잘 되었는지 어떻게 확인할 수 있나요?', 11 | answer: 12 | 'festa를 통해 확인하실 수 있습니다. 추가적으로 참가 신청을 완료하신 분들에게는 리마인드 메일을 보내드릴 예정입니다.', 13 | }, 14 | { 15 | question: '행사 정원이 몇 명인가요?', 16 | answer: 17 | '본 행사는 오프라인으로 진행되며 공간과 좌석의 한계로 인해 행사 정원은 30명입니다. 참가자분들의 양해 부탁드립니다.', 18 | }, 19 | { 20 | question: '티켓 양도가 가능한가요?', 21 | answer: 22 | '행사 당일 불가피하게 당사자 참가가 어려울 경우에 한 해 티켓 양도가 가능합니다. 단 참가자 굿즈는 사전 제작으로 변경이 불가능합니다.', 23 | }, 24 | ], 25 | ON_SITE: [ 26 | { 27 | question: '발표 시간보다 늦게 도착할 것 같습니다. 괜찮을까요?', 28 | answer: 29 | '네. 늦게 오셔도 4층 등록 데스크에서 등록 확인 후 입장하실 수 있습니다.', 30 | }, 31 | { 32 | question: '행사 당일 현장 등록이 가능한가요?', 33 | answer: 34 | '사전에 참가자들의 굿즈를 제작해 행사 당일 전달드릴 예정입니다. 공지된 신청 기간 이후에는 등록이 불가능하며 현장 등록도 불가능합니다.', 35 | }, 36 | ], 37 | EVENT: [ 38 | { 39 | question: '추후에 발표 영상이 공개되나요?', 40 | answer: 41 | '본 행사는 영상 녹화를 따로 진행하지 않을 예정입니다. 참가자분들의 양해 부탁드립니다.', 42 | }, 43 | ], 44 | ETC: [ 45 | { 46 | question: '음식물 반입이 가능한가요?', 47 | answer: 48 | '행사 당일 1인 1음료가 제공될 예정이며 이외 음식물은 반입이 어렵습니다.', 49 | }, 50 | ], 51 | } 52 | 53 | export const CodeOfConductData: FAQType[] = [ 54 | { 55 | question: '1. 서로를 존중해 주세요.', 56 | answer: 57 | '건강한 의견 공유를 위해 타인의 권리를 침해하거나 타인에게 불쾌감을 줄 수 있는 발언을 금지합니다. 성적 묘사, 스토킹, 프라이버시 침해 등의 타인에게 불쾌감을 줄 수 있는 발언은 금지되며 인종, 외모, 장애, 정치, 종교 등의 개인에 대한 차별적인 발언 또한 금지됩니다.', 58 | }, 59 | { 60 | question: '2. 적극적으로 참여해 주세요.', 61 | answer: 62 | '‘누구나’ 참여할 수 있는 컨퍼런스입니다. 저희 스태프는 이를 위하여 프로그램들을 준비하였으며 원활한 진행을 위해 참여자 여러분의 적극적 참여를 부탁드립니다.', 63 | }, 64 | { 65 | question: '3. 타인의 프라이버시를 지켜주세요.', 66 | answer: 67 | '참가자분들 중에는 사진 촬영을 원하지 않는 분들도 있을 수 있으니 타인의 얼굴이 포함되는 사진은 최대한 자제 부탁드립니다.', 68 | }, 69 | { 70 | question: '4. 도움이 필요하시면 자유롭게 요청하세요.', 71 | answer: 72 | '만약 행사 도중 도움이 필요하시면 주위에 도움을 요청하세요. 또한 도움을 요청받으셨다면 주저하지 말고 도와주세요.', 73 | }, 74 | ] 75 | -------------------------------------------------------------------------------- /data/first/Staff.ts: -------------------------------------------------------------------------------- 1 | import Teo from '@/public/images/staff/Teo.png' 2 | import Domo from '@/public/images/staff/Domo.png' 3 | import Halang from '@/public/images/staff/Halang.png' 4 | import Hubble from '@/public/images/staff/Hubble.png' 5 | import J from '@/public/images/staff/J.png' 6 | import Moseung from '@/public/images/staff/Moseung.png' 7 | import Raul from '@/public/images/staff/Raul.png' 8 | import Sudal from '@/public/images/staff/Sudal.png' 9 | import Three from '@/public/images/staff/Three.png' 10 | import Juni from '@/public/images/staff/Juni.png' 11 | import Mini from '@/public/images/staff/Mini.png' 12 | import { StaticImageData } from 'next/image' 13 | 14 | type StaffDataType = { 15 | id: number 16 | name: string 17 | description: string 18 | image: StaticImageData 19 | url: string 20 | position: 'developer' | 'designer' 21 | } 22 | 23 | export const StaffData: StaffDataType[] = [ 24 | { 25 | id: 1, 26 | name: '테오', 27 | description: '시니어 웹 프론트엔드 개발자입니다', 28 | image: Teo, 29 | url: 'https://github.com/developer-1px', 30 | position: 'developer', 31 | }, 32 | { 33 | id: 2, 34 | name: '도모', 35 | description: '대화와 협업을 중요하게 생각하는 개발자', 36 | image: Domo, 37 | url: 'https://github.com/kdomo', 38 | position: 'developer', 39 | }, 40 | { 41 | id: 3, 42 | name: '하랭', 43 | description: '유저에게 더 좋은 경험을 주고 싶은 개발자', 44 | image: Halang, 45 | url: 'https://github.com/haryung-lee', 46 | position: 'developer', 47 | }, 48 | { 49 | id: 4, 50 | name: '허블', 51 | description: '망원경처럼 넓은 시야를 가진 개발자', 52 | image: Hubble, 53 | url: 'https://github.com/Circlewee', 54 | position: 'developer', 55 | }, 56 | { 57 | id: 5, 58 | name: '제리', 59 | description: '꾸준히 기록하며 성장하는 개발자', 60 | image: J, 61 | url: 'https://github.com/dmswl98', 62 | position: 'developer', 63 | }, 64 | { 65 | id: 6, 66 | name: '모승', 67 | description: '같이 걷는 개발자, 사람과 개발을 좋아하는 개발자', 68 | image: Moseung, 69 | url: 'https://github.com/endmoseung', 70 | position: 'developer', 71 | }, 72 | { 73 | id: 7, 74 | name: '라울', 75 | description: '성장하는 개발자 라울입니다.', 76 | image: Raul, 77 | url: 'https://github.com/key-del-jeeinho', 78 | position: 'developer', 79 | }, 80 | { 81 | id: 8, 82 | name: '수달', 83 | description: '수다와 호기심 많은 개발자', 84 | image: Sudal, 85 | url: 'https://github.com/headring', 86 | position: 'developer', 87 | }, 88 | { 89 | id: 9, 90 | name: '쓰리', 91 | description: '혼자보다는 함께 성장하고 싶은 개발자', 92 | image: Three, 93 | url: 'https://github.com/Lee-Yeseul', 94 | position: 'developer', 95 | }, 96 | { 97 | id: 10, 98 | name: '주니', 99 | description: '형태와 맥락을 연결하는 디자이너', 100 | image: Juni, 101 | url: 'https://www.behance.net/zooni06', 102 | position: 'designer', 103 | }, 104 | { 105 | id: 11, 106 | name: '미니', 107 | description: '안녕하세요. 미니입니다.', 108 | image: Mini, 109 | url: 'https://github.com/skmn3', 110 | position: 'developer', 111 | }, 112 | ] 113 | -------------------------------------------------------------------------------- /data/first/Talk.ts: -------------------------------------------------------------------------------- 1 | type TalkData = { 2 | contents: string 3 | speaker: string 4 | } 5 | 6 | export const talkData: TalkData[] = [ 7 | { 8 | contents: `내 경험을 이야기할 수 있는\n기회가 있으면 좋겠다`, 9 | speaker: '3년차 주니어 FE개발자', 10 | }, 11 | { 12 | contents: '다른 주니어는\n어떻게 성장하고 있을까?', 13 | speaker: '갓 취업한 신입 개발자', 14 | }, 15 | { 16 | contents: '나와 비슷한 경험을\n먼저 겪은 사람은 없을까?', 17 | speaker: '주니어 BE개발자', 18 | }, 19 | { 20 | contents: '사이드 프로젝트\n팀원을 구하고 싶은데...', 21 | speaker: 'FE개발자', 22 | }, 23 | ] 24 | -------------------------------------------------------------------------------- /data/second/FAQ.ts: -------------------------------------------------------------------------------- 1 | import { FAQ as FAQType } from '@/types' 2 | 3 | interface FAQData { 4 | [key: string]: FAQType[] 5 | } 6 | 7 | export const faqData: FAQData = { 8 | APPLY: [ 9 | { 10 | question: '등록이 잘 되었는지 어떻게 확인할 수 있나요?', 11 | answer: 12 | '정상적으로 등록된 경우 점핏으로부터 안내 메일과 점핏 카카오톡 채널을 통해 알림톡이 발송됩니다. 추가 상세한 내용은 별도로 안내 예정입니다.', 13 | }, 14 | { 15 | question: '행사 정원이 몇 명인가요?', 16 | answer: 17 | '본 행사는 오프라인으로 진행되며 공간과 좌석의 한계로 인해 행사 정원은 50명입니다. 참가자분들의 양해 부탁드립니다.', 18 | }, 19 | { 20 | question: '티켓 양도가 가능한가요?', 21 | answer: 22 | '행사 당일 불가피하게 당사자 참가가 어려울 경우에 한해 양도가 가능합니다. 단, 참가자 굿즈는 사전 제작으로 변경이 불가능하다는 점은 기억해주세요.', 23 | }, 24 | ], 25 | ON_SITE: [ 26 | { 27 | question: 28 | '처음부터 조로 나눠져서 진행된다면 함께 온 친구와 떨어져야 하는 건가요?', 29 | answer: 30 | '함께 온 일행과 함께 같은 조가 되어야 한다면 입장 당일 스태프에게 알려주시면 자리는 다시 조정을 해드리겠습니다.', 31 | }, 32 | { 33 | question: '당일 현장 등록할 수 있나요?', 34 | answer: 35 | '사전에 참가자들의 굿즈를 제작해 행사 당일 전달드릴 예정입니다. 공지된 신청 기간 이후에는 등록이 불가능하며 현장 등록도 불가능합니다.', 36 | }, 37 | { 38 | question: '행사 당일 주차 지원이 가능할까요?', 39 | answer: 40 | '행사 당일 주차 지원은 어렵습니다. 따라서 가급적 대중교통 이용을 권장드립니다.', 41 | }, 42 | { 43 | question: '발표 시간보다 늦게 도착할 것 같습니다. 괜찮을까요?', 44 | answer: 45 | '네, 늦게 오셔도 등록 데스크에서 등록 확인 후 입장하실 수 있습니다.', 46 | }, 47 | ], 48 | EVENT: [ 49 | { 50 | question: '추후에 발표 영상이 공개되나요?', 51 | answer: 52 | '본 행사는 영상 녹화를 따로 진행하지 않을 예정입니다. 참가자분들의 양해 부탁드립니다.', 53 | }, 54 | ], 55 | ETC: [ 56 | { 57 | question: '음식물 반입이 가능한가요?', 58 | answer: '행사 내 음식물은 반입이 어렵습니다.', 59 | }, 60 | ], 61 | } 62 | 63 | export const CodeOfConductData: FAQType[] = [ 64 | { 65 | question: '1. 서로를 존중해 주세요.', 66 | answer: 67 | '건강한 의견 공유를 위해 타인의 권리를 침해하거나 타인에게 불쾌감을 줄 수 있는 발언을 금지합니다. 성적 묘사, 스토킹, 프라이버시 침해 등의 타인에게 불쾌감을 줄 수 있는 발언은 금지되며 인종, 외모, 장애, 정치, 종교 등의 개인에 대한 차별적인 발언 또한 금지됩니다.', 68 | }, 69 | { 70 | question: '2. 적극적으로 참여해 주세요.', 71 | answer: 72 | '‘누구나’ 참여할 수 있는 컨퍼런스입니다. 저희 스태프는 이를 위하여 프로그램들을 준비하였으며 원활한 진행을 위해 참여자 여러분의 적극적 참여를 부탁드립니다.', 73 | }, 74 | { 75 | question: '3. 타인의 프라이버시를 지켜주세요.', 76 | answer: 77 | '참가자분들 중에는 사진 촬영을 원하지 않는 분들도 있을 수 있으니 타인의 얼굴이 포함되는 사진은 최대한 자제 부탁드립니다.', 78 | }, 79 | { 80 | question: '4. 도움이 필요하시면 자유롭게 요청하세요.', 81 | answer: 82 | '만약 행사 도중 도움이 필요하시면 주위에 도움을 요청하세요. 또한 도움을 요청받으셨다면 주저하지 말고 도와주세요.', 83 | }, 84 | ] 85 | -------------------------------------------------------------------------------- /data/second/Talk.ts: -------------------------------------------------------------------------------- 1 | type TalkData = { 2 | contents: string 3 | speaker: string 4 | className?: string 5 | } 6 | 7 | export const talkData: TalkData[] = [ 8 | { 9 | contents: `내 경험을 이야기할 수 있는\n기회가 있으면 좋겠다`, 10 | speaker: '3년차 주니어 FE개발자', 11 | className: 'gap-6', 12 | }, 13 | { 14 | contents: '다른 주니어는\n어떻게 성장하고 있을까?', 15 | speaker: '갓 취업한 신입 개발자', 16 | }, 17 | { 18 | contents: '나와 비슷한 경험을\n먼저 겪은 사람은 없을까?', 19 | speaker: '주니어 BE개발자', 20 | }, 21 | { 22 | contents: '사이드 프로젝트\n팀원을 구하고 싶은데...', 23 | speaker: 'FE개발자', 24 | className: 'gap-0', 25 | }, 26 | ] 27 | -------------------------------------------------------------------------------- /data/third/EventDetails.ts: -------------------------------------------------------------------------------- 1 | export const eventDetails = { 2 | name_ko: 'TEOConf 2024', 3 | name_en: 'TEOConf 2024', 4 | date_saturday: '11.23. 토요일', 5 | date_sunday: '11.24. 일요일', 6 | location: '팀스파르타 본사', 7 | } 8 | -------------------------------------------------------------------------------- /data/third/FAQ.ts: -------------------------------------------------------------------------------- 1 | import { FAQ as FAQType } from '@/types' 2 | 3 | interface FAQData { 4 | [key: string]: FAQType[] 5 | } 6 | 7 | export const faqData: FAQData = { 8 | APPLY: [ 9 | { 10 | question: '당일 현장 등록할 수 있나요?', 11 | answer: 12 | '아니오, 사전에 참가자들의 굿즈를 제작해 행사 당일 전달 드릴 예정입니다. 공지된 신청 기간 이후에는 등록이 불가하며 현장 등록도 불가합니다.', 13 | }, 14 | { 15 | question: '티켓 양도가 가능한가요?', 16 | answer: 17 | '네, 행사 당일 불가피하게 본인 참가가 어려울 경우에 한해 양도가 가능합니다. 단, 참가자 굿즈는 사전 제작으로 변경이 불가능하다는 점은 기억해주세요.', 18 | }, 19 | { 20 | question: 21 | '처음부터 조로 나눠져서 진행된다면 함께 온 친구와 떨어져야 하는 건가요?', 22 | answer: 23 | '네, 아쉬움은 뒤로 하고, 이참에 새로운 사람과 네트워킹 해보는 것은 어떨까요?', 24 | }, 25 | { 26 | question: '발표 시간보다 늦게 도착할 것 같습니다. 괜찮을까요?', 27 | answer: 28 | '네, 늦게 오셔도 등록 데스크에서 등록 확인 후 입장하실 수 있습니다.', 29 | }, 30 | ], 31 | ON_SITE: [ 32 | { 33 | question: 34 | '처음부터 조로 나눠져서 진행된다면 함께 온 친구와 떨어져야 하는 건가요?', 35 | answer: 36 | '함께 온 일행과 함께 같은 조가 되어야 한다면 입장 당일 스태프에게 알려주시면 자리는 다시 조정을 해드리겠습니다.', 37 | }, 38 | { 39 | question: '당일 현장 등록할 수 있나요?', 40 | answer: 41 | '사전에 참가자들의 굿즈를 제작해 행사 당일 전달드릴 예정입니다. 공지된 신청 기간 이후에는 등록이 불가능하며 현장 등록도 불가능합니다.', 42 | }, 43 | { 44 | question: '행사 당일 주차 지원이 가능할까요?', 45 | answer: 46 | '행사 당일 주차 지원은 어렵습니다. 따라서 가급적 대중교통 이용을 권장드립니다.', 47 | }, 48 | { 49 | question: '발표 시간보다 늦게 도착할 것 같습니다. 괜찮을까요?', 50 | answer: 51 | '네, 늦게 오셔도 등록 데스크에서 등록 확인 후 입장하실 수 있습니다.', 52 | }, 53 | ], 54 | EVENT: [ 55 | { 56 | question: '추후에 발표 영상이 공개되나요?', 57 | answer: 58 | '본 행사는 영상 녹화를 따로 진행하지 않을 예정입니다. 참가자분들의 양해 부탁드립니다.', 59 | }, 60 | ], 61 | ETC: [ 62 | { 63 | question: '음식물 반입이 가능한가요?', 64 | answer: '행사 내 음식물은 반입이 어렵습니다.', 65 | }, 66 | ], 67 | } 68 | 69 | export const CodeOfConductData: FAQType[] = [ 70 | { 71 | question: '호칭은 닉네임으로, 극존칭 대신 부드러운 말투로 대화해요.', 72 | answer: 73 | '참가자 분들은 본인의 이름이 아닌 닉네임으로 소통하며 서로를 부를 때는 “~님”을 사용하지 않아요.\n그렇지만 상호 존대의 의미로 “요”라는 어미는 사용하되, “시”, “~께서”와 같은 극존칭은 사용하지 않아요.\n\nex)\n테오님은 이런 경우에 어떻게 하세요? (X)\n테오는 이런 경우에 어떻게 해요? (O)\n\n처음에는 살짝 어색하겠지만 조금만 익숙해지면 훨씬 더 편하게 많은 사람들과 이야기를 나누게 되는 경험을 하시게 될 거예요.', 74 | }, 75 | { 76 | question: '참여자를 존중해요.', 77 | answer: 78 | '행사 진행 중 성적 묘사, 스토킹, 프라이버시 침해, 컨퍼런스를 방해하는 행위, 외모, 연령, 경력, 언어에 대한 차별 또는 위 행동을 옹호하는 행위는 모두 금지돼요. 위 행동을 위반한 경우 경고 및 퇴장 처리가 이루어질 수 있어요.', 79 | }, 80 | { 81 | question: '적극적으로 참여해요.', 82 | answer: 83 | 'TEOConf 2024는 누구나 참여할 수 있는 컨퍼런스를 지향해요. 적극적으로 참여해서 함께 더 알찬 컨퍼런스를 만들어요. 혹시 관련해서 어려움이 있다면 스태프에게 말해주세요.', 84 | }, 85 | { 86 | question: '타인의 프라이버시를 지켜줘요.', 87 | answer: 88 | '누군가는 사진 촬영을 원하지 않을 수 있어요. 타인의 얼굴이 포함되는 사진 촬영은 자제해주세요.', 89 | }, 90 | ] 91 | -------------------------------------------------------------------------------- /data/third/Links.ts: -------------------------------------------------------------------------------- 1 | export const links = { 2 | linkedin: 'https://www.linkedin.com/company/teoconf', 3 | instagram: 'https://www.instagram.com/teo_conf/', 4 | twitter: 'https://x.com/teoconf', 5 | HHTeoConfPage: 6 | 'https://hanghae99.spartacodingclub.kr/blog/teoconf-2024-with-항해-플러스-33755', 7 | submitForm: 8 | 'https://hanghae99.spartacodingclub.kr/blog/teoconf-2024-with-항해-플러스-33755', 9 | } 10 | -------------------------------------------------------------------------------- /data/third/Menu.ts: -------------------------------------------------------------------------------- 1 | export type Menu = { 2 | id: string 3 | name: string 4 | children?: Array 5 | link?: string 6 | } 7 | 8 | export const menu: Array = [ 9 | { name: '프로그램', id: 'program' }, 10 | { name: '타임테이블', id: 'timetable' }, 11 | { name: '장소', id: 'location' }, 12 | { 13 | name: 'FAQ', 14 | id: 'faq', 15 | children: [ 16 | { name: '자주 묻는 질문', id: 'faq1' }, 17 | { name: '행동강령', id: 'faq2' }, 18 | ], 19 | }, 20 | { 21 | name: '지난 행사', 22 | id: 'passed-confs', 23 | children: [ 24 | { name: '테오콘 1기', id: 'sub1', link: 'first' }, 25 | { name: '테오콘 2기', id: 'sub2', link: 'second' }, 26 | ], 27 | }, 28 | ] 29 | -------------------------------------------------------------------------------- /data/third/Session.ts: -------------------------------------------------------------------------------- 1 | import Byeongsker from '@/public/images/speakers/third/Byeongsker.jpg' 2 | import Donghun from '@/public/images/speakers/third/Donghun.jpg' 3 | import Happy from '@/public/images/speakers/third/Happy.jpg' 4 | import Hyan from '@/public/images/speakers/third/Hyan.jpg' 5 | import Kimcheomji from '@/public/images/speakers/third/Kimcheomji.jpg' 6 | import Kimsan from '@/public/images/speakers/third/Kimsan.jpg' 7 | import AndrewYu from '@/public/images/speakers/third/AndrewYu.jpeg' 8 | 9 | export const sessions = [ 10 | { 11 | date: '11.23. 토요일', 12 | speakers: [ 13 | { 14 | title: '30명의 심리 싸움, 오프라인 주식게임 제작기', 15 | name: '하이안', 16 | image: Hyan, 17 | resource: 18 | 'https://drive.google.com/file/d/1dOI2bsc_q2Dj8Wq_nUwQ-6-sbA8jrSDy/view?usp=drive_link', 19 | }, 20 | { 21 | title: 22 | 'Trunk Code Quality로 일관성 있는 코드를 쓰는 개발자 되기', 23 | name: '앤드류', 24 | image: AndrewYu, 25 | resource: 26 | 'https://drive.google.com/file/d/1gCJ48ZMUU64d9YkcczwgRqWlkevh4pu6/view?usp=drive_link', 27 | }, 28 | { 29 | title: '출근해서 바로 써먹을 수 있는 커뮤니케이션 팁', 30 | name: '동훈', 31 | image: Donghun, 32 | resource: 33 | 'https://speakerdeck.com/donghoon759/culgeunhaeseo-baro-sseo-meogeul-su-issneun-keomyunikeisyeon-tib', 34 | }, 35 | { 36 | title: 37 | '주니어 개발자의, 200일간 혼자만의 짧은 글쓰기 챌린지로 성장하기', 38 | name: '병스커', 39 | image: Byeongsker, 40 | resource: 41 | 'https://drive.google.com/file/d/1i2GMA9CuxSKmFoLDe6Tvwzosci4NOuNM/view?usp=sharing', 42 | }, 43 | ], 44 | }, 45 | { 46 | date: '11.24. 일요일', 47 | speakers: [ 48 | { 49 | title: '30명의 심리 싸움, 오프라인 주식게임 제작기', 50 | name: '하이안', 51 | image: Hyan, 52 | resource: 53 | 'https://drive.google.com/file/d/1dOI2bsc_q2Dj8Wq_nUwQ-6-sbA8jrSDy/view?usp=drive_link', 54 | }, 55 | { 56 | title: '어댑터 아키텍처를 통해 클라이언트 환경 개선하기', 57 | name: '해피', 58 | image: Happy, 59 | resource: 60 | 'https://drive.google.com/file/d/1oLdu81OLpLNN-2N9rLqgMLFmppwdMWfw/view?usp=drive_link', 61 | }, 62 | { 63 | title: 'EventManager 패턴을 활용하여 상태를 전역처럼 관리하기', 64 | name: '김첨지', 65 | image: Kimcheomji, 66 | resource: 67 | 'https://gamma.app/docs/Copy-of-EventManager--jgf7myoa0j9h37y', 68 | }, 69 | { 70 | title: '대용량 파일 쉽게 다루기 A-Z', 71 | name: '토마토', 72 | image: Kimsan, 73 | resource: 74 | 'https://drive.google.com/file/d/1av6ey5xwjZwo9Njimig3dj2-ltFt-HwT/view?usp=drive_link', 75 | }, 76 | ], 77 | }, 78 | ] 79 | -------------------------------------------------------------------------------- /data/third/Sponsor.ts: -------------------------------------------------------------------------------- 1 | export const sponsor = { 2 | link: 'https://hanghae99.spartacodingclub.kr/', 3 | name: '항해 플러스', 4 | } 5 | -------------------------------------------------------------------------------- /data/third/Staff.ts: -------------------------------------------------------------------------------- 1 | import Hubble from '@/public/images/staff/Hubble.png' 2 | import Teo from '@/public/images/staff/Teo.png' 3 | import Casey from '@/public/images/staff/third/Casey.jpg' 4 | import Field from '@/public/images/staff/third/Field.jpg' 5 | import Jyan from '@/public/images/staff/third/Jyan.jpg' 6 | import Mincho from '@/public/images/staff/third/Mincho.jpg' 7 | import Seoltang from '@/public/images/staff/third/Seoltang.jpg' 8 | import Solssak from '@/public/images/staff/third/Solssak.jpg' 9 | import Sooya from '@/public/images/staff/third/Sooya.jpg' 10 | import { StaticImageData } from 'next/image' 11 | 12 | type StaffDataType = { 13 | id: number 14 | name: string 15 | description: string 16 | image: StaticImageData 17 | url: string 18 | position: 'developer' | 'designer' 19 | } 20 | 21 | export const StaffData: StaffDataType[] = [ 22 | { 23 | id: 1, 24 | name: '테오', 25 | description: '시니어 웹 프론트엔드 개발자입니다', 26 | image: Teo, 27 | url: 'https://github.com/developer-1px', 28 | position: 'developer', 29 | }, 30 | { 31 | id: 2, 32 | name: '케시', 33 | description: '생각을 현실로 만드는 개발자', 34 | image: Casey, 35 | url: 'https://github.com/junny97', 36 | position: 'developer', 37 | }, 38 | { 39 | id: 3, 40 | name: '필드', 41 | description: '든든한 서포터, 근데 이제 개발을 곁들인', 42 | image: Field, 43 | url: 'https://github.com/kimfield98', 44 | position: 'developer', 45 | }, 46 | { 47 | id: 4, 48 | name: '쟌', 49 | description: '매일매일이 고점인 사람', 50 | image: Jyan, 51 | url: 'https://github.com/bbbjihan', 52 | position: 'developer', 53 | }, 54 | { 55 | id: 5, 56 | name: '민초', 57 | description: '함께 고민하는 것을 좋아하는 개발자', 58 | image: Mincho, 59 | url: 'https://github.com/chhw130', 60 | position: 'developer', 61 | }, 62 | { 63 | id: 6, 64 | name: '설탕', 65 | description: '가치를 만드는 개발자', 66 | image: Seoltang, 67 | url: 'https://github.com/seoltang', 68 | position: 'developer', 69 | }, 70 | { 71 | id: 7, 72 | name: '솔싹', 73 | description: '늘 즐거운 개발자', 74 | image: Solssak, 75 | url: 'https://github.com/solssak', 76 | position: 'developer', 77 | }, 78 | { 79 | id: 8, 80 | name: '수야', 81 | description: '사용자 중심의 개발 철학을 실천하는 개발자', 82 | image: Sooya, 83 | url: 'https://github.com/soojjung', 84 | position: 'developer', 85 | }, 86 | { 87 | id: 9, 88 | name: '허블', 89 | description: '망원경처럼 넓은 시야를 가진 개발자', 90 | image: Hubble, 91 | url: 'https://github.com/Circlewee', 92 | position: 'developer', 93 | }, 94 | ] 95 | -------------------------------------------------------------------------------- /data/third/Talk.ts: -------------------------------------------------------------------------------- 1 | type TalkData = { 2 | contents: string 3 | speaker: string 4 | className?: string 5 | } 6 | 7 | export const talkData: TalkData[] = [ 8 | { 9 | contents: `내 경험을 이야기할 수 있는\n기회가 있으면 좋겠다`, 10 | speaker: '3년차 주니어 FE개발자', 11 | className: 'gap-6', 12 | }, 13 | { 14 | contents: '다른 주니어는\n어떻게 성장하고 있을까?', 15 | speaker: '갓 취업한 신입 개발자', 16 | }, 17 | { 18 | contents: '나와 비슷한 경험을\n먼저 겪은 사람은 없을까?', 19 | speaker: '주니어 BE개발자', 20 | }, 21 | { 22 | contents: '사이드 프로젝트\n팀원을 구하고 싶은데...', 23 | speaker: 'FE개발자', 24 | className: 'gap-0', 25 | }, 26 | ] 27 | -------------------------------------------------------------------------------- /lib/observer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { useEffect, useRef } from 'react' 3 | let direction = '' 4 | let prevYposition = 0 5 | 6 | // scroll 방향 check function 7 | const checkScrollDirection = (prevY: number) => { 8 | if (window.scrollY === 0 && prevY === 0) return 9 | else if (window.scrollY > prevY) direction = 'down' 10 | else direction = 'up' 11 | 12 | prevYposition = window.scrollY 13 | } 14 | 15 | const useIntersectionObservation = (setActiveId: any, currentId: any) => { 16 | const contentRef = useRef({}) 17 | 18 | useEffect(() => { 19 | const callback = (observedContent: any) => { 20 | observedContent.forEach((content: any) => { 21 | contentRef.current[content.target.id] = content 22 | }) 23 | 24 | const visibleContent: any = Object.values(contentRef.current).filter( 25 | (content: any) => content.isIntersecting 26 | ) 27 | checkScrollDirection(prevYposition) 28 | setActiveId(visibleContent[0]?.target.id) 29 | } 30 | //1. 새로운 observer 설정 31 | const observer = new IntersectionObserver(callback, { 32 | threshold: [0.05, 1], 33 | }) 34 | 35 | //2. DOM 요소 찾고 Observer달아주기 36 | const contents: any = [...document.querySelectorAll('.content')] 37 | 38 | contents.forEach((content: any) => observer.observe(content)) 39 | 40 | //3. 언 마운트시 옵저버 해제 41 | return () => observer.disconnect() 42 | }, []) 43 | } 44 | 45 | export default useIntersectionObservation 46 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | 3 | // const isDev = process.env.NODE_ENV === 'development' 4 | // const basePath = isDev ? '' : '/TEOConf-FE' 5 | // const assetPrefix = isDev ? '' : '/TEOConf-FE/' 6 | 7 | const nextConfig = { 8 | reactStrictMode: true, 9 | output: 'export', 10 | images: { 11 | remotePatterns: [ 12 | { 13 | protocol: 'https', 14 | hostname: 'velog.velcdn.com', 15 | port: '', 16 | pathname: '/images/**', 17 | }, 18 | { 19 | protocol: 'https', 20 | hostname: 'i.imgur.com', 21 | port: '', 22 | pathname: '/**', 23 | }, 24 | { 25 | protocol: 'https', 26 | hostname: 'img1.daumcdn.net', 27 | port: '', 28 | pathname: '/thumb/**', 29 | }, 30 | { 31 | protocol: 'https', 32 | hostname: 'blog.kakaocdn.net', 33 | port: '', 34 | pathname: '/dna/**', 35 | }, 36 | ], 37 | unoptimized: true, 38 | }, 39 | basePath: '', 40 | assetPrefix: '', 41 | } 42 | module.exports = nextConfig 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "teo-2023-fe", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "preview": "npx serve out -p 3000", 11 | "build-static": "next build", 12 | "postbuild-static": "shx touch out/.nojekyll", 13 | "deploy": "npm run build-static && npm run postbuild-static && gh-pages -d out -t true" 14 | }, 15 | "dependencies": { 16 | "@emotion/react": "^11.10.6", 17 | "@emotion/styled": "^11.10.6", 18 | "@mui/icons-material": "^5.11.16", 19 | "@mui/material": "^5.12.2", 20 | "@types/node": "18.16.0", 21 | "@types/react": "18.2.0", 22 | "@types/react-dom": "18.2.0", 23 | "autoprefixer": "10.4.14", 24 | "clsx": "^1.2.1", 25 | "gh-pages": "^5.0.0", 26 | "next": "13.3.1", 27 | "postcss": "8.4.23", 28 | "react": "18.2.0", 29 | "react-dom": "18.2.0", 30 | "react-hook-form": "^7.43.9", 31 | "swiper": "^9.4.0", 32 | "tailwindcss": "3.3.1", 33 | "typescript": "5.0.4" 34 | }, 35 | "devDependencies": { 36 | "eslint": "8.39.0", 37 | "eslint-config-next": "13.3.1", 38 | "eslint-plugin-prettier": "^4.2.1", 39 | "prettier": "^2.8.8", 40 | "shx": "^0.4.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pages/2025/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from '@/components/2025-commingsoon/shared/Header/Header' 2 | import MetaHead from '@/components/2025-commingsoon/shared/MetaHead' 3 | import Hero from '@/components/2025-commingsoon/sections/Hero' 4 | import Introduce from '@/components/2025-commingsoon/sections/Introduce' 5 | import Overview from '@/components/2025-commingsoon/sections/Overview' 6 | import Sponsor from '@/components/2025-commingsoon/sections/Sponsor' 7 | import SNS from '@/components/2025-commingsoon/sections/SNS' 8 | import Footer from '@/components/2025-commingsoon/sections/Footer' 9 | 10 | export default function Home() { 11 | return ( 12 |
    13 | 14 |
    15 | 16 | 17 | 18 | 19 | 20 |
    21 |
    22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import type { AppProps } from 'next/app' 3 | 4 | declare global { 5 | interface Window { 6 | kakao: any 7 | } 8 | } 9 | 10 | export default function App({ Component, pageProps }: AppProps) { 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from 'next/document' 2 | import { getPath } from '@/utils/path' 3 | 4 | export default function Document() { 5 | return ( 6 | 7 | 8 | 12 | 13 | 14 |
    15 | 16 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /pages/first/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from '@/components/first/Header' 2 | import FAQ from '@/components/first/FAQ' 3 | import Introduce from '@/components/first/Introduce' 4 | import Program from '@/components/first/Program' 5 | import Networking from '@/components/first/NetWorking' 6 | import Gallery from '@/components/first/Gallery' 7 | import NameCard from '@/components/first/NameCard' 8 | import Sponsor from '@/components/first/Sponsor' 9 | import Map from '@/components/first/Map' 10 | import SNS from '@/components/first/SNS' 11 | import Staff from '@/components/first/Staff' 12 | import Footer from '@/components/first/Footer' 13 | import Banner from '@/components/first/Banner' 14 | import MetaHead from '@/components/first/common/MetaHead' 15 | import CodeOfConduct from '@/components/first/CodeOfConduct' 16 | 17 | export default function Home() { 18 | return ( 19 | <> 20 | 21 |
    22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |