Type error: Cannot find module ... Github Actions Ubuntu 환경

이미지
  Github Actions로 배포 자동화를 설정하기 위해서 yml 파일을 작성하고 push 했는데 위에 사진과 같이 파일 경로를 찾지 못하는 문제가 발생했다. 로컬 개발환경에서 build시에는 문제가 없는데 Github Actions에서 build시에만  Type error: Cannot find module '@/assets/icons' or its corresponding type declarations. 오류가 발생했다. 오류 해결 과정에서 세웠던 가설들과 해결 과정 3가지를 적어보려한다. 첫번째, 가설:  Next.js 13에서는 터보팩으로 전환했지만 여전히 Webpack을 번들러로 사용하고. Webpack은 TS 컴파일 단계와 다른 단계에서 실행되므로 tsconfig.json을 고려하지 않는다. 그러므로  tsconfig.json의 paths가 제대로 적용되지 않는것이 문제이다. 해결방법: next.config.js에 webpack config.resolve.alias 옵션을 사용해서 @를 경로에 맞게 수정되도록 설정한다. /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: false , webpack ( config ) { config . resolve . alias = { ... config . resolve . alias , '@' : path . join ( __dirname , '/' ), }; return { ... config ,    ...        ... 기타 설정 생략         ... ... 결론: 하지만 나의 경우  동일하게 Cannot find moudle 에러가 발생했다. 두번째,  가설: typescript는 devDependencies에 작성되어 설치되는데 build 할 때 환

ios 웹뷰(WebView) 노치 및 하단바 이슈 그리고 안전영역(Safe Area)

이미지
안전 영역(Safe Area)은 무엇인가? 안전 영역은 텔레비전 화면에서 볼 수 있는 텔레비전 화면의 영역을 설명하기 위해 텔레비전 제작에서 사용되는 용어다. 텔레비전 화면 해상도 비율이 다양해지면서 영상의 제목, 자막 등 필수 콘텐츠 노출을 보장할 수 있는 영역 으로 사용되는 영역이다. safe area wiki:  https://en.wikipedia.org/wiki/Safe_area_(television) iphone에서의 안전 영역(Safe Area) iphone 8 이하 기종과 달리 iphone x 이후 기종부터는 하드웨어가 변경되면서 화면의 viewport 기준이 변경되었다. iphone 8 viewport iphone 14pro max viewport  viewport 기준이 변경되면서 아래 두가지 문제가 발생했다. 1. 화면 끝의 라운딩 처리로 콘텐츠가 잘리면서 제대로 노출되지 못하는 문제 2. 화면 상단 노치 및 화면 하단에 indicator bar가 viewport에 포함되어 콘텐츠가 겹치는 문제 오른쪽  iphone 14pro max viewport 사진을 보면 화면 끝 라운딩된 부분에 콘텐츠가 잘리고 하단 indicator bar와 콘텐츠가 겹치면서 콘텐츠가 가려지는 문제가 발생하는것을 확인할 수 있다. 또한 따로 설정하지 않으면 아래 사진과 같이 가로 모드 시 표시한 박스부분 및 하단 indicator bar 또한 콘텐츠가 잘리거나 겹쳐서 가려지는 문제가 발생한다. 기본적으로 웹은 브라우저 상단에 네이게이션 고정영역이 있으므로 해당 부분은 콘텐츠가 잘리는 문제를 신경쓰지 않아도 된다. 하지만 앱 내 웹뷰의 경우 상단 네이게이션 고정영역이 없어 위에서 언급한 문제점들이 발생하게된다. CSS env() 속성 사용해서 문제 해결하기 CSS 속성인 env()를 사용하여 상단 노치, 하단 indicator bar, 가로 모드 시 좌우 잘리게되는 부분의 값을 설정할 수 있어 위에서 언급한 문제점들을 모두 해결할 수 있다. - iOS 11.2 버전

[CSS] :nth-of-type과 :nth-child 차이점

  가상 클래스 선택자(pseudo-class selector)  :nth-of-type과 :nth-child 비교 :nth-of-type 선택자로 지정된 요소와 일치하는 형제 요소 중 n번째인 요소를 선택한다. See the Pen css - nth-of-type by seungwonleee ( @seungwon-code ) on CodePen . div 부모 태그 안에 1번째 p 태그 자식 요소와 2번째 p 태그 자식 요소를 선택한다. h1과 h2 태그는 p 태그가 아니므로 순서에 영향을 받지 않는다. :nth-child 형제 요소 중 n번째인 요소를 선택한다. 만약 해당 순서의 요소와 선택자로 지정된 요소가 일치하지 않는 경우 선택되지 않는다. See the Pen Untitled by seungwonleee ( @seungwon-code ) on CodePen . div 부모 태그 안에  1번째 p 태그와 2번째 p 태그를 선택하지만 1번째 요소가 h1 태그이므로 선택되지않아 css가 적용되지 않으며 2번째 요소는 p 태그이므로 css가 적용된다.

Error: Invalid value for attribute height="15rem" (Safari 브라우저)

이미지
  Safari 브라우저에서 svg의  width와 height에 rem을 사용할때 발생되는 Error 이다. 다른 브라우저에서는 Error가 안보이지만 Safari 브라우저에서만 해당 Error가 보인다. Error가 발생해도 동작은 잘 되지만 그래도 Error니까 해결해보자. 원인은 rem 사용 때문이다. 이전에는 크롬에서도 rem 사용시 문제가 되었던것같은데 이제는 문제가 없고 Safari 브라우저에서만 해당 오류가 발생하는것같았다. Safari 브라우저에서 해당 Error를 없애기 위해서는 svg width와 height에는 number, px ,percentage 만 사용하라고 한다. 그러면 rem을 사용한 반응형으로 어떻게 만들지? 라는 생각부터 든다. 해결법은 간단하다. const ArrowBackIcon = ({ width = 24 , height = 24 , color }: IconProps ) => { return ( < SVGContainer width = { width } height = { height } > < ArrowBack width = { '100%' } height = { '100%' } fill = { color } /> </ SVGContainer > ); }; export default ArrowBackIcon ; ArrowBack은 svg 아이콘이고 svg 파일에 width와 height는 current가 작성되어있는 상태이다. SVGContainer는 div 태그이며 svg width와 height 크기를 rem으로 해당 태그에 할당한다. 이렇게 할당하면 원하는 사이즈대로 반응형으로 svg를 표현할 수 있다. Safari 브라우저 콘솔에서도 Error가 사라진것을 확인할 수 있다.

HTML 엔티티(entity)

이미지
  HTML에서 몇몇 특수문자를 사용하려고하면 아래와 같이 오류가 발생한다. html은 "< " 와 같은 특수문자를 사용하면 태그를 작성하려고 한다고 생각해서 이와 같이 오류가 발생된다. 나는 태그를 작성하려는게 아니라 단지 방향을 표시하고 싶은건데 말이다. 위와 같은 특수문자를 화면에 동일하게 출력하면서 위 처럼 오류가 발생하지 않게 해주는것이 HTML 엔티티(entity)이다. HTMl entity를 사용해서 아래와 같이 작성하면 우리가 위에서 작성한것과 같이 "<- 이쪽 방향을 봐주세요"가 화면에 동일하게 출력되는것을 확인할 수 있다. < p > &lt; - 이쪽 방향을 봐주세요. </ p > ; 특수문자 뿐만아니라 공백(띄어쓰기)과 같은 문제도 HTMl entity를 사용해서 해결 할 수 있다.  < p > &lt; - 이쪽 방향을 봐주세요. </ p > ; 위와같이 html을 작성해도 우리 화면에는 "<- 이쪽 방향을 봐주세요" 이렇게 텍스트가 붙어서 출력된다. 이런 공백(띄어쓰기)를 표현하기 위해서는  &nbsp; 엔티티를 사용하면 된다. < p > &lt; - &nbsp;&nbsp;&nbsp;&nbsp; 이쪽 &nbsp;&nbsp;&nbsp;&nbsp; 방향을 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 봐주세요. </ p > ; 이렇게 작성해주면 우리가 원하던대로 "<-             이쪽          방향을        봐주세요" 이렇게 공백(띄어쓰기)을 제대로 화면에 출력하게 된다. 이외에도 다양한 문자들을 아래 문법을 사용해서 표현할 수 있다. &엔티티이름; 또는 &#엔티티숫자; 대표적으로 많이

[React] onKeyDown 이벤트 한글 마지막 글자 한번 더 입력 오류

이미지
  예전에 프로젝트하면서 해쉬태그 관련 컴포넌트를 만들었던적이 있어서 그대로 가져와서 이번 프로젝트에서 사용하려했는데 onKeyPress 이벤트가 Deprecated 되었다고 나와서 MDN 가이드대로 keydown 으로 수정해보았다. 여기서 문제가 발생했는데 onKeyPress 이벤트에서는 아무 문제가 없는데 onKeyDown 이벤트를 사용했을때 동영상에서 처럼 마지막 글자가 한번더 출력되는 문제가 발생했다. 내가 작성한 setState와 관련한 비동기적인 동작 순서를 내가 잘못생각해서 발생되는 문제인줄 알았는데 찾아보니 해당 문제는 크롬 브라우저에서 한글을 입력하는 경우만 해당 문제가 발생하는것을 알게됐다. 키보드 이벤트 발생시 이벤트 핸들러가 두번 호출되는 문제였다. 한글의 경우 자음과 모음의 조합으로 한 음절이 만들어지는 조합 문자이기 때문에 글자가 조합중인지, 조합이 끝난 상태인지를 알 수 없기 때문에 발생하는 문제였다. 해당 문제는 isComposing 이라는 값을 체크해 해결할 수 있었다. 그런데 여기서 또 하나의 문제 event.isComposing 또는 event.target.isComposing을 사용하려고해도 해당 메소드가 없다고 나왔다. 예전에 React 공식 문서에서 브라우저 고유 이벤트가 필요하다면 nativeEvent 어트리뷰트를 참조하라는 글을 본적이 있었다. 아래 링크를 첨부했다. 아래처럼 코드를 작성하면 아래 동영상처럼 해결이 된다. 문제 해결 완료! 참고하면 좋을만한 링크 https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3 https://ko.reactjs.org/docs/events.html#overview

Nodemailer 설정하기 (보안 수준이 낮은 앱의 액세스 기능 지원 중단)

이미지
개인프로젝트를 진행하면서 사용자 이메일 인증, 임시 비밀번호 발급 등을 위해 Nodemailer 라이브러리를 사용했다.  나의 경우 google Gmail 이메일 계정을 사용하여 Nodemailer를 사용했다. 예전에는 Nodemailer 공식 문서와 같이 코드를 작성하고 구글 계정에서 보안 수준이 낮은 앱의 액세스 기능을 키기만 하면 됐었다. 하지만 정책이 변경되어 다르게 설정이 필요해서 이와 관련해서 작성해보려한다. 1. 자신의 프로필 -> [Google 계정 관리] -> 보안 페이지 -> [Google에 로그인] 중 [2단계 인증]을 클릭 후 자신의 모바일 또는 sms 인증이 가능한 기기를 이용하여 2단계 인증을 완료시킨다. 2. 2단계 인증을 완료하고 바로 아래 앱 비밀번호를 클릭하여 비밀번호를 발급받는다. 해당 비밀번호는 창을 닫으면 다시는 확인할 수 없다. 따로 저장해두거나 새로 발급받아야 한다. 앱 선택은 기타를 선택해도 되고 아무거나 선택해도 된다. 3. 발급받은 번호를 기존의 Nodemailer 설정의 pass에 입력해주면 설정이 끝이난다. 추가적으로 Gmail 고객센터에서 제공하는 문서이다.  * 변경된 정책 확인 https://support.google.com/accounts/answer/6010255?hl=ko * 메일이 스팸으로 분류되는것을 방지하기 https://support.google.com/mail/answer/81126?hl=ko

[TypeORM] database update시 save() 메서드를 사용하면 주의할점

#  Updating in the database Now let's load a single photo from the database, update it and save it: import { Photo } from "./entity/Photo" import { AppDataSource } from "./index" const photoRepository = AppDataSource . getRepository ( Photo ) const photoToUpdate = await photoRepository . findOneBy ( { id : 1 , } ) photoToUpdate . name = "Me, my friends and polar bears" await photoRepository . save ( photoToUpdate ) Now photo with   id = 1   will be updated in the database.   공식 문서를 보면 위와 같이 사용자 정보를 최초로 저장할때가 아닌 업데이트 시에도 사용자 객체를 가져와 참조 값을 변경하고 save() 메서드를 사용해서 사용자 정보를 업데이트하도록 나와있다. 공식문서 첫페이지에 나와있는 방법이기에 그대로 그냥 사용했었다. 그런데 프로젝트 진행중 알 수 없는 문제가 발생했다. 데이터를 업데이트하는데 업데이트 되지않고 값이 추가적으로 insert 되는 상황이 발생했다. MySQL Workbench를 보면 값 업데이트가아닌 추가 되었는데 다시 해당 오류를 재현해보려했지만 동일한 오류를 재현하지 못했다. typeorm 처음 공부할때 공식문서와 여러 블로그를 봤는데 save() 메서드를 주의해서 사용해야 한다는 내용이있었다. 다시 찾아보니 TypeORM에서 save() 메서드는 entity에 작성되어있는 연결되어있는 모든 PK값을 WHERE 절에 함께 담아 SELECT 쿼리를 실행하

error TS2339: Property 'id' does not exist on type 'User'.

이미지
  express.js, passport.js, typescript 를 사용해서 로그인 기능을 구현했다. passport local 로그인 동작 순서는 아래와 같다. passport-local -> passport.authenticate -> local strategy -> req.login -> passport.serializeUser -> response 각 순서에서 작업을 수행하고 각 단계에서 callback 함수와 함께 다음 함수에서 필요한 값을 넘겨주게 된다.  error TS2339: Property 'id' does not exist on type 'User'. 해당 에러는 아래 코드에서 발생되었다. passport . serializeUser (( user , done ) => { done ( null , user . id ); }); 아래 코드처럼 req.login으로 user 정보를 넘겨passport.serializeUser에서 해당 유저 정보를 받는데 return req . login ( user , async ( err ) => { if ( err ) { console . error ( err ); return next ( err ); } if ( typeof user === 'object' && user !== null ) { if ( user ) { return res . status ( 200 ). json ({ success : true , message : 'login success' , user : { email : user . email , nickname : u

[TypeORM] Error during Data Source initialization: DataTypeNotSupportedError: Data type "Object" in "User.google" is not supported by "mysql" database.

이미지
오류 사진 import { Entity , PrimaryGeneratedColumn , Column , OneToMany } from 'typeorm' ; import { Post } from './Post' ; @ Entity () export class User { @ PrimaryGeneratedColumn () id : number ; @ Column ({ unique : true , length : 50 }) email : string ; @ Column ({ unique : true , nullable : true }) google : string | null ; @ Column ({ length : 10 }) nickname : string ; @ Column ({ length : 200 }) password : string ; @ Column () createdAt : Date ; @ OneToMany (() => Post , ( post ) => post . user ) posts : Post []; } - 수정 전 - 구글 로그인으로 가입한 사용자 정보를 받는 컬럼에서 오류가 발생했다. Object 데이터 타입을 받을 수 없다는것인데 찾아보니 유니언 타입(Union type)으로 작성하면 데이터 타입을 Object로 인식하기 때문에 위에서 google: string | null 과 같이 작성하면 안된다고 한다. import { Entity , PrimaryGeneratedColumn , Column , OneToMany } from 'typeorm' ; import { Post } from './Post' ; @ Entity () export class User { @ PrimaryGeneratedColumn () id : number ; @ Column ({