diff --git a/.env b/.env new file mode 100644 index 00000000..ef47618a --- /dev/null +++ b/.env @@ -0,0 +1 @@ +NEXT_PUBLIC_NAVER_CLIENT_ID=c467awv8uh \ No newline at end of file diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..ef47618a --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_NAVER_CLIENT_ID=c467awv8uh \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..ef47618a --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_NAVER_CLIENT_ID=c467awv8uh \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index d0c9c6a4..713f33ec 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,27 +1,35 @@ module.exports = { env: { - browser: true, es2021: true, node: true, + jest: true, }, extends: [ 'eslint:recommended', - 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', + 'plugin:@next/next/recommended', + 'plugin:storybook/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { - ecmaFeatures: { - jsx: true, - }, ecmaVersion: 13, sourceType: 'module', }, - plugins: ['react', '@typescript-eslint'], + plugins: ['@typescript-eslint'], + ignorePatterns: ['/public', 'shared/types/service-worker.d.ts'], rules: { - indent: ['error', 'space'], 'linebreak-style': ['error', 'unix'], quotes: ['error', 'single'], semi: ['error', 'always'], + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error'], }, + overrides: [ + { + files: ['**/*.test.ts'], + env: { + jest: true, + }, + }, + ], }; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..4f062d7d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @hyunjinee @HanCiHu \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/rfc-issue-template.md b/.github/ISSUE_TEMPLATE/RFC.md similarity index 100% rename from .github/ISSUE_TEMPLATE/rfc-issue-template.md rename to .github/ISSUE_TEMPLATE/RFC.md diff --git a/.github/ISSUE_TEMPLATE/basic-issue-template.md b/.github/ISSUE_TEMPLATE/basic-issue-template.md index 12f81a7a..1b068bd0 100644 --- a/.github/ISSUE_TEMPLATE/basic-issue-template.md +++ b/.github/ISSUE_TEMPLATE/basic-issue-template.md @@ -1,21 +1,21 @@ --- name: Basic Issue Template about: 이슈 등록시 기본으로 사용되는 템플릿입니다. -title: "[JIRA 하위 이슈 제목]" -labels: "" -assignees: "" +title: '[JIRA 하위 이슈 제목]' +labels: '' +assignees: '' --- -### 이슈 등록 전 확인사항 ( 확인하고 지워주시면 됩니다 🙂 ) + ## 🌴 ** [유저 스토리 제목] 예시) GIT 라벨 정하기 ** -내용 + ## 📋 **태스크 리스트** diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0ecd221b..e2055ca5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,17 +1,16 @@ -### 개요 +### 개요 #이슈번호 (MOZI-번호) -- 업무에 대한 설명 -- 연관된 이슈 + ### 작업 사항 - [ ] 작업 사항 -- [ ] 테스트 코드 작성 ### 변경후 -- 변경 후 작동 화면 캡쳐 or 동영상 + ### 기타 -- 코드 리뷰시 중점적으로 봐줬으면 하는 부분 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..1caa61f4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,74 @@ +name: CI + +on: [push] + +defaults: + run: + working-directory: ./ + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v2 + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v3 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + **/node_modules + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Test + run: yarn test + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v2 + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v3 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + **/node_modules + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Lint + run: yarn lint + + prettier: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v2 + + - name: Prettify code + uses: creyD/prettier_action@v4.2 + with: + dry: True + github_token: ${{ secrets.PERSONAL_GITHUB_TOKEN }} diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml new file mode 100644 index 00000000..0dbfe52a --- /dev/null +++ b/.github/workflows/lighthouse.yml @@ -0,0 +1,102 @@ +name: CI + +on: [push] + +jobs: + lighthouseci: + runs-on: ubuntu-latest + permissions: write-all + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Use Node.js 16.15.1 + uses: actions/setup-node@v3 + with: + node-version: 16.15.1 + - name: npm install, build + run: | + npm install + npm run build + - name: run Lighthouse CI + run: | + npm install -g @lhci/cli@0.8.x + lhci autorun + env: + LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} + - name: Format lighthouse score + id: format_lighthouse_score + uses: actions/github-script@v3 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const fs = require('fs'); + const results = JSON.parse(fs.readFileSync("./lhci_reports/manifest.json")); + const pages = ["404", "500", "_offline", "index", "map"] + let comments = "⚡️ MOZI Lighthouse 성능 측정 결과 ⚡️" + "\n"; + results.forEach((result, i) => { + const { summary, jsonPath } = result; + const details = JSON.parse(fs.readFileSync(jsonPath)); + const { audits } = details; + + const formatResult = (res) => Math.round(res * 100); + Object.keys(summary).forEach( + (key) => (summary[key] = formatResult(summary[key])) + ); + const score = (res) => (res >= 90 ? "🟢" : res >= 50 ? "🟠" : "🔴"); + + const comment = [ + `${pages[i]} page`, + `| Category | Score |`, + `| --- | --- |`, + `| ${score(summary.performance)} Performance | ${summary.performance} |`, + `| ${score(summary.accessibility)} Accessibility | ${summary.accessibility} |`, + `| ${score(summary["best-practices"])} Best-Practices | ${summary["best-practices"]} |`, + `| ${score(summary.seo)} SEO | ${summary.seo} |`, + `| ${score(summary.pwa)} PWA | ${summary.pwa} |` + ].join("\n"); + + const detail = [ + `| Category | Score |`, + `| --- | --- |`, + `| ${score( + audits["first-contentful-paint"].score * 100 + )} First Contentful Paint | ${ + audits["first-contentful-paint"].displayValue + } |`, + `| ${score( + audits["largest-contentful-paint"].score * 100 + )} Largest Contentful Paint | ${ + audits["largest-contentful-paint"].displayValue + } |`, + `| ${score( + audits["first-meaningful-paint"].score * 100 + )} First Meaningful Paint | ${ + audits["first-meaningful-paint"].displayValue + } |`, + `| ${score( + audits["speed-index"].score * 100 + )} Speed Index | ${ + audits["speed-index"].displayValue + } |`, + `| ${score( + audits["total-blocking-time"].score * 100 + )} Total Blocking Time | ${ + audits["total-blocking-time"].displayValue + } |`, + ].join("\n"); + + comments += comment + "\n" +"\n"+ detail + "\n" + "\n"; + }); + + core.setOutput('comments', comments) + + - uses: jwalton/gh-find-current-pr@v1 + id: finder + - uses: marocchino/sticky-pull-request-comment@v2 + with: + number: ${{ steps.finder.outputs.pr }} + message: | + ${{ github.sha }} + Lighthouse CI ended successfully. + ${{ steps.format_lighthouse_score.outputs.comments}} diff --git a/.gitignore b/.gitignore index 67045665..00c12018 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,6 @@ typings/ .yarn-integrity # dotenv environment variables file -.env .env.test # parcel-bundler cache (https://parceljs.org/) @@ -102,3 +101,26 @@ dist # TernJS port file .tern-port + +.env.local +.env.dev +.env.prod + +# service worker + +**/public/workbox-*.js +**/public/workbox-*.js.map +**/public/sw.js +**/public/sw.js.map +**/public/worker-*.js + +# next build files +build/ +fallback-*.js + +# lighthouse +.lighthouseci +lhci_reports + +# worker files +worker-* \ No newline at end of file diff --git a/.lighthouserc.js b/.lighthouserc.js new file mode 100644 index 00000000..683790f8 --- /dev/null +++ b/.lighthouserc.js @@ -0,0 +1,13 @@ +module.exports = { + ci: { + collect: { + staticDistDir: '.next/', + numberOfRuns: 1, + }, + upload: { + target: 'filesystem', + outputDir: './lhci_reports', + reportFilenamePattern: '%%PATHNAME%%-%%DATETIME%%-report.%%EXTENSION%%', + }, + }, +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..dd449725 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.md diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000..c22fe10b --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,25 @@ +const path = require('path'); +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); + +const toPath = (_path) => path.join(process.cwd(), _path); + +module.exports = { + stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: '@storybook/react', + core: { + builder: '@storybook/builder-webpack5', + }, + webpackFinal: async (config) => { + return { + ...config, + resolve: { + ...config.resolve, + plugins: [new TsconfigPathsPlugin({})], + alias: { + ...config.resolve.alias, + }, + }, + }; + }, +}; diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 00000000..bebf6f7e --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,20 @@ +import { addDecorator } from '@storybook/react'; +import { GlobalStyle } from '../styles/globalStyle'; + +addDecorator((story) => ( + <> + + {story()} + +)); + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + layout: 'fullscreen', +}; diff --git a/README.md b/README.md index 655f7593..65077b61 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# mozi-client -mozi client repository + + +# MOZI + +MOZI는 할 일을 잊지 않기 위한 시간/장소 기반 TODO서비스 입니다. + +## How To Start + +### Node version +`v16.15.1` + +### Develop Mode +```sh +yarn install + +yarn dev +``` + +### Production Mode +```sh +yarn build + +yarn start +``` \ No newline at end of file diff --git a/components/common/AppLayout/index.tsx b/components/common/AppLayout/index.tsx new file mode 100644 index 00000000..245c5ff8 --- /dev/null +++ b/components/common/AppLayout/index.tsx @@ -0,0 +1,17 @@ +import { Container } from './styles'; +import Sidebar from '@/components/common/Sidebar'; + +interface AppLayoutProps { + children: React.ReactNode; +} + +const AppLayout: React.FC = ({ children }) => { + return ( + + + {children} + + ); +}; + +export default AppLayout; diff --git a/components/common/AppLayout/styles.tsx b/components/common/AppLayout/styles.tsx new file mode 100644 index 00000000..a434d5e2 --- /dev/null +++ b/components/common/AppLayout/styles.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + position: relative; + display: flex; + + width: 100%; + height: 100%; + + overflow: hidden; +`; diff --git a/components/common/Button/index.stories.tsx b/components/common/Button/index.stories.tsx new file mode 100644 index 00000000..414f5224 --- /dev/null +++ b/components/common/Button/index.stories.tsx @@ -0,0 +1,12 @@ +import { Meta, Story } from '@storybook/react'; + +import Button, { ButtonProps } from '.'; + +export default { + component: Button, + title: 'Button', +} as Meta; + +const Template: Story = (args) =>