티스토리 뷰
DATE_ADD, CURRENT_DATE , NOW() 를 insert 하려고 했는데 null 이 나와버렸다.
[예제코드]
async createOwnedCoupon(user: Users, newCoupon: CreateOwnedCouponDto) {
try {
// 1. 등록할 쿠폰이 존재하는지 확인
const coupon = await this.couponsRepository
.createQueryBuilder('coupons')
.where('couponId = :couponId', { couponId: newCoupon.couponId })
.getOne();
if (!coupon) {
throw new NotFoundException('해당 쿠폰은 존재하지 않습니다.');
}
// 쿠폰(Coupon)을 ownedCoupon에 등록
await await this.userOwnCouponsRepository
.createQueryBuilder('ownedCoupons')
.insert(OwnedCoupons)
.values({
expirationDate: `DATE_ADD(CURRENT_DATE, INTERVAL %{coupon.validPeriod} DAY)`,
UserId: user.userId,
CouponId: newCoupon.couponId,
});
// 새로운 쿠폰정보를 추가한 후 현재유저의 보유쿠폰 리스트를 리스폰스합니다.
const couponList = await this.userOwnCouponsRepository
.createQueryBuilder('ownedCoupons')
.where('UserId = :userId', { userId: user.userId })
.andWhere('usedDate is null') // 아직 사용 안한 쿠폰
.getMany();
return couponList;
} catch (error) {
// 에러가 발생하면 롤백
throw error;
}
}
typeorm에서 날짜데이터를 insert 하려고했더니...
expirationDate 에 null 값으로 매핑된다...
values({
expirationDate: `DATE_ADD(CURRENT_DATE, INTERVAL %{coupon.validPeriod} DAY)`,
});
DATE_ADD 나, CURRENT_DATE 는 select 문에서는 조회할 수 있어도, insert문에서는 값이 안나온다...
SELECT CURRENT_DATE(); // 2022-11-03
SELECT CURRENT_DATE; // 2022-11-03
SELECT NOW(); // 2022-11-03 21:00:46
SELECT DATE_ADD( CURRENT_DATE(), INTERVAL 14 DAY ); // 2022-11-17
SELECT DATE_ADD( CURRENT_DATE, INTERVAL 14 DAY ); // 2022-11-17
SELECT DATE_ADD( NOW(), INTERVAL 14 DAY ); // 2022-11-17 21:02:16
[참고]
여기에 대한 이유를 구글링을 해봐도 못찾아서 다른 방법을 모색했다.
그러면 new Date() 로 하면 되지 않느냐 하지만
Date() 객체를 얻어서 계산을 해야하고 타임포맷을 맞춰야하는 작업이 있어서 이방법을 선택하지 않았다.
js-joda 가 무엇?
- javascript 의 Date는 불변성 보장을 하지 않는다.
- 백엔드에서 날짜타입은 불변성 보장을 필수로 해야한다.
- Js-joda 는 날짜데이터에 있어서 불변성을 보장한다.
- 타임존도 지원한다.
https://jojoldu.tistory.com/610
https://jojoldu.tistory.com/600
async createOwnedCoupon(user: Users, newCoupon: CreateOwnedCouponDto) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 1. 등록할 쿠폰이 존재하는지 확인
const coupon = await this.couponsRepository
.createQueryBuilder('coupons')
.where('couponId = :couponId', { couponId: newCoupon.couponId })
.getOne();
if (!coupon) {
throw new NotFoundException('해당 쿠폰은 존재하지 않습니다.');
}
// 2. 쿠폰발행날짜(issuedDate): 쿠폰 등록 요청 시기로 default값 으로 세팅
const issuedDate = LocalDateTime.now();
// 3. 쿠폰만료날짜(expirationDate): null -> "쿠폰발행날짜 + 쿠폰기간" 으로 변경
const expirationDate = issuedDate.plusDays(coupon.validPeriod);
// 4. 쿠폰(Coupon)을 ownedCoupon에 등록
await queryRunner.manager.getRepository(OwnedCoupons).save({
issuedDate,
expirationDate,
UserId: user.userId,
CouponId: newCoupon.couponId,
});
// 데이터베이스에 저장
queryRunner.commitTransaction();
// 5. 새로운 쿠폰정보를 추가한 후 현재유저의 보유쿠폰 리스트를 리스폰스합니다.
const couponList = await this.userOwnCouponsRepository
.createQueryBuilder('ownedCoupons')
.where('UserId = :userId', { userId: user.userId })
.andWhere('usedDate is null') // 아직 사용 안한 쿠폰
.getMany();
return couponList;
} catch (error) {
// 에러가 발생하면 롤백
await queryRunner.rollbackTransaction();
throw error;
}
}
}
일단 그냥 불러서 해봤더니... insert에서 에러가 발생하여 데이터추가가 안된다.
디버깅을 해보니 js-joda로
쿠폰발급날짜(issuedDate) 와 쿠폰만료날짜(expirationDate) 값이 나왔다.
그런데 신기하게도 겉으로는 {YYYY}-{mm}-{dd}T{h}:{m}:{s}.{ms} 구조로 보이지만
_date필드와 _time 필드가 존재했다.
디버그 콘솔에서는 다음과 같은 에러가 떴다.
ERROR [ExceptionsHandler] Column count doesn't match value count at row 1
위 에러의 원인은 insert 하려는 데이터필드는 8개인데, 값은 10개인것이다!
데이터를 등록할때 _date, _time 도 같이 포함해서 등록하는 거다.
INSERT INTO `ownedCoupons`(
`ownedCouponId`,
`issuedDate`,
`usedDate`,
`expirationDate`,
`isExtendDate`,
`UserId`,
`CouponId`,
`OrderId`
) VALUES (
DEFAULT,
`_date` = '2022-11-03',
`_time` = '21:08:16.130',
DEFAULT,
`_date` = '2022-11-10',
`_time` = '21:08:16.130',
DEFAULT,
5,
1,
DEFAULT
)"
그러면 요구하는 데이터 필드의 개수에 맞추기 위해서는
_date , _time 으로 분리된 것들을 다시 하나로 합쳐서 issuedDate 값으로 나타내야한다.
여기서는 객체를 직렬화 시켜야한다.
그럴려면 ValueTransformer 가 필요하다. 참고 블로그를 보면서 따라해봤다..
[ LocalDateTimeTransformer ]
LocalDateTimeTransformer을 만들었는데...
DateTimeUtil 은 참고 블로그 작성자가 만든 객체이다.
import { LocalDateTime } from 'js-joda';
import { ValueTransformer } from 'typeorm';
export class LocalDateTimeTransformer implements ValueTransformer {
// entity -> db 로 넣을 때
to(entityValue: LocalDateTime): Date {
return DateTimeUtil.toDate(entityValue);
}
// db -> entity 로 가져올 때
from(databaseValue: Date): LocalDateTime {
return DateTimeUtil.toLocalDateTime(databaseValue);
}
}
[ DateTimeUtil ]
참고 블로그에도 toLocalDateTimeBy() 만 정의되어있지..
toDate(), toLocalDateTime() 는 뒤져봐도 안보인다. 어떻게 만들어야될지 생각이 나지 않았다 ㅠㅠ
import { DateTimeFormatter, LocalDateTime } from 'js-joda';
export class DateTimeUtil {
private static DATE_FORMATTER = DateTimeFormatter.ofPattern('yyyy-MM-dd');
private static DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm:ss');
static toLocalDateTimeBy(strDate: string): LocalDateTime {
if (!strDate) {
return null;
}
return LocalDateTime.parse(strDate, DateTimeUtil.DATE_TIME_FORMATTER);
}
}
js-joda 공식 다큐먼트
js-joda timezone
convert js-joda -> Date
일단은 검색은 해봤는데.. 그래도 감이 오지않았고 과제가 쌓이고 시간이 부족해서 시도해보지 못했다.
다른방법으로 찾아봐야겠다...
현재 내 수준에 있어서 js-joda가 너무 어려웠고.. 나중에 리팩토링할때 도전해봐야겠다...ㅜㅜ!
번외지만 mysql datetime issue도 있다..
https://github.com/typeorm/typeorm/issues/7430
[보충설명] 직렬화 와 역직렬화?
직렬화(Serialization)
- 객체를 직렬화하여 전송가능한 파일 형태로 만드는 것.
- 객체들의 데이터를 연속적인 데이터로 변형하여 Stream을 통해 데이터를 읽도록 한다.
- 객체들을 통째로 파일로 저장하거나 전송할 때 주로 사용.
역직렬화(Deserialization)
- 직렬화된 파일 등을 다시 객체의 형태로 만드는 것
- 저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원한다.
[참고]
momentjs를 사용
상황1: moment() 부분에서 에러발생
import moment from 'moment';
async createOwnedCoupon(user: Users, newCoupon: CreateOwnedCouponDto) {
...
// 2. 쿠폰발행날짜(issuedDate): 쿠폰 등록 요청 시기로 default값 으로 세팅
const now = moment();
const issuedDate = now.format('yyyy-MM-dd HH:mm:ss');
// 3. 쿠폰만료날짜(expirationDate): null -> "쿠폰발행날짜 + 쿠폰기간" 으로 변경
const expirationDate = now.add(coupon.validPeriod, 'day').format('yyyy-MM-dd HH:mm:ss');
console.log(issuedDate);
console.log(expirationDate);
...
}
발생에러: ERROR [ExceptionsHandler] (0 , moment_1.default) is not a function
해결방안
import * as moment from 'moment-timezone';
상황2 : 타입포맷이 이상한 현상
moment().format('yyyy-MM-dd HH:mm:ss') 로 타임포맷을 정했으나
디버깅 해보니까 issuedDate와 , expirationDate 모두 '2022-11-Fr 01:40:47' 로 출력된다.
내가 원하는 타임포맷은 '2022-11-04 01:44:05' 와 같은 형식이다.
현재 mysql 8.0의 타임스탬프도 내가 원하는 형식으로 되어있기 때문이다.
해결방안: 타임포맷을 'YYYY-MM-DD HH:mm:ss' 로 고치면된다 :)
[참고]
'Backend > 꾸준히 TIL' 카테고리의 다른 글
[에러 해결] Cannot find module bcrypt_lib.node (0) | 2022.11.05 |
---|---|
[NestJS + Typescript ] URL 깨짐 원인과 typeorm where in (1) | 2022.11.04 |
Type number trivaially inferred from a number literal, remove type annotation 에러 해결 (0) | 2022.11.03 |
package-lock.json 잦은 변경을 막고 싶어요. (0) | 2022.10.29 |
[AWS RDS & TypeORM] 에러해결하기 & 코드리뷰 (0) | 2022.10.29 |
- Total
- Today
- Yesterday
- MySQL
- TDD
- jest
- 클린아키텍쳐
- 참고
- 한달독서
- 디지털디톡스
- OS
- git
- gem
- RDBMS
- 나도 할 수 있다
- 한달어스
- node.js
- Nest.js
- Jekyll
- TypeScript
- MongoDB
- 갓생살자
- 습관개선
- 개발용어
- nestjs jest
- 스마트폰중독
- nestjs
- 바이트디그리
- IT용어
- Mongoose
- 미완
- vscode
- typeORM
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |