-
Notifications
You must be signed in to change notification settings - Fork 4
๐ ํ ์คํธ ์ฝ๋์ ๋ชจํน
๊ธฐ์กด์๋ In-memory๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์์์ผ๋, ํ์ฌ๋ MySQL๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋๋ก ์์ ํ์์
๊ทธ๋ฌ๋ ์์ง mergeํ์ง ์์๊ณ , ์ด๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ด๋ ค์ด ์ํ์ด๋ค.
- local์ ์๋ ํ๋ก ํธ ์ฝ๋๋ก ๊ฒ์ฆ : ํ์ฌ๋
random-nameapi์์ ์๋ฌ๊ฐ ๋ฐ์ํ์ฌ ๊ฒ์ฆํ๊ธฐ ํ๋ ์ํฉ - postman์ผ๋ก ๊ฒ์ฆ : ์๋์ผ๋ก ํ์ธํด์ผ ํจ
๊ทธ๋์ ์ด๋ฅผ ํด๊ฒฐํ๊ณ ์ unit test ์ฝ๋๋ฅผ ์์ฑํ๋ ค๊ณ ํ๋ค.
์ค์ DB์ ๊ฐ์ด ์ ์ฅ๋๋ฉด ์ ๋๋ฏ๋ก, Mock DB๋ฅผ ์ฌ์ฉํ๋ค. ๋์ปค๋ฅผ ํตํด Testcontainer๋ฅผ ๋์ฐ๊ณ ํ
์คํธํ๋ ๋ฐฉ๋ฒ๋ ์์ผ๋ ์๊ฐ ๊ด๊ณ์ ํจ์คํ๋ค.
๊ธฐ๋ณธ์ ์ธ ์ปจ์ : ๋น๊ต๊ตฐ(StudyRoomRepository)์ ๋์กฐ๊ตฐ(Repository)๋ฅผ ๋์ด ์ด๋ฅผ ๋น๊ตํ๋ค.
// ๋น๊ต๊ตฐ
let studyRoomRepository: StudyRoomRepository;
// ๋์กฐ๊ตฐ
let repository: Repository<StudyRoom>;-
Test.createTestingModule์ ํตํด ํ ์คํธ ๋ชจ๋์ ์ค์ ํ๋ค. -
providers๋ฅผ ํตํด ๋น๊ต๊ตฐ๊ณผ ๋์กฐ๊ตฐ์ ์์กด์ฑ์ ์ฃผ์ ํ๋ค.- ๋น๊ต๊ตฐ : ๊ทธ๋๋ก ๋ฃ๋๋ค.
- ๋์กฐ๊ตฐ : ์ค์ DB๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋๋ฏ๋ก, ๊ธฐ๋ณธ Repository์
StudyRoomentity์ ๋ํ ์์กด์ฑ์ ์ฃผ์ ํ๋ค.-
getRepositoryToken: TypeORM์์ ํน์ ํ ์ํฐํฐ์ ๋ํ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ์๋ณํ๊ธฐ ์ํ ํ ํฐ์ ์๋ฏธํ๋ค. ํ์ฌ ํ ์คํธ ์ฝ๋์์๋StudyRoomRepository๋ฅผ ํ ์คํธํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํด๋น ์ํฐํฐ์ ๋์ํ๋StudyRoom์ ๋ํ ํ ํฐ์ ์ฌ์ฉํ๋ค. -
useClass: TypeORM์์ ์ด๋ค class(ํ์ฌ ํ ์คํธ์์๋ repository์ ํด๋นํ๋ค)๋ฅผ ์ฌ์ฉํ ๊ฒ์ธ์ง ์ค์ ํ๋ ์ต์ ์ด๋ค.
-
-
compile์ ํตํด ์ค์ ํ ํ ์คํธ ๋ชจ๋์ ์์ฑํ๊ณ , ํ์ํ ์์กด์ฑ์ ๋ชจ๋ ์ฃผ์ ํ๋ค. - ์์์ ์์ฑ๋ ํ ์คํธ ๋ชจ๋์ ์ค์ repository์ ์ฃผ์ ํ๋ค.
let studyRoomRepository: StudyRoomRepository;
let mockRepository: jest.Mocked<Repository<StudyRoom>>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
StudyRoomRepository,
{
provide: getRepositoryToken(StudyRoom),
useValue: {
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
},
},
],
}).compile();
studyRoomRepository = module.get<StudyRoomRepository>(StudyRoomRepository);
mockRepository = module.get<Repository<StudyRoom>>(
getRepositoryToken(StudyRoom),
) as jest.Mocked<Repository<StudyRoom>>;
});-
์ฅ์
- DB ์ฐ๊ฒฐ ์์ด ๋น ๋ฅด๊ฒ ํ ์คํธ ๊ฐ๋ฅ
- ์ธ๋ถ ์ข ์์ฑ์ ์ต์ํํ๋ค.
-
๋จ์
- ์ค์ DB ๋์์ ๊ฒ์ฆํ์ง๋ ์๊ณ , ๋จ์ ์์กด์ฑ ์ฃผ์ ์ฉ์ด๋ค.
- ํน์ ๋ฐํ๊ฐ์ ์ํ๋ค๋ฉด
mock.fn์ผ๋ก ์ค์ ํด์ผ ํ๋ค.
- ๋จ์ ํ ์คํธํ ๋ ์ ํฉํ๋ค.
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:', // ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ
entities: [StudyRoom], // ์ํฐํฐ ๋ฑ๋ก
synchronize: true, // ์คํค๋ง ๋๊ธฐํ
}),
TypeOrmModule.forFeature([StudyRoomRepository]), // ํ
์คํธํ ๋ ํฌ์งํ ๋ฆฌ ๋ฑ๋ก
],
}).compile();
studyRoomRepository = module.get<StudyRoomRepository>(StudyRoomRepository);
});-
์ฅ์
- TypeORM์ ์ค์ ๋์์ ๊ฒ์ฆํ ์ ์๋ค.
- ๋ฉ๋ชจ๋ฆฌ ๋ด์์ ์๋ํด์ ๋น ๋ฅด๊ณ , ํ ์คํธ ๊ฐ ๋ฐ์ดํฐ ๊ฐ์ญ์ด ์๋ค.
- TypeORM ์คํค๋ง ๋๊ธฐํ(
synchronize: true)๋ก ํ ์ด๋ธ ์๋ ์์ฑ ๊ฐ๋ฅ
-
๋จ์
- ์ค์ ์ด ๋ณต์กํ๋ค.
- ํตํฉ ํ ์คํธํ ๋ ์ ํฉํ๋ค.
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [DataRepository],
}).compile();
dataRepository = module.get(DataRepository);
});-
์ฅ์
- ์ค์ DB์ ๊ฐ์ฅ ๊ทผ์ ํ ํ ์คํธ
- TypeORM์ ๋์ ๋ฟ๋ง ์๋๋ผ ์ฟผ๋ฆฌ ์คํ ๊ฒฐ๊ณผ๊น์ง ํ์ธ ๊ฐ๋ฅ
- ์ธ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์์ ๋์์ ์ ํํ ๊ฒ์ฆ
-
๋จ์
- ์ธ๋ถ DB์ ์์กดํ๋ฏ๋ก ์ธ๋ถ ์์กด์ฑ์ด ๋๋ค.
- ํ ์คํธ ํ๊ฒฝ ์ค์ ์ด ๋ณต์กํ๋ค.
- ํ ์คํธ ์๋๊ฐ ๋๋ฆฌ๋ค.
- ๋ค๋ฅธ ํ ์คํธ์ ๋ฐ์ดํฐ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
- End-to-End ํ ์คํธ์ ์ ํฉํ๋ค.
beforeAll(async () => {
const container = await new GenericContainer("mysql")
.withExposedPorts(3306)
.withEnv("MYSQL_ROOT_PASSWORD", "root")
.withEnv("MYSQL_DATABASE", "test_db")
.start();
const port = container.getMappedPort(3306);
const host = container.getHost();
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host,
port,
username: 'root',
password: 'root',
database: 'test_db',
entities: [StudyRoom],
synchronize: true,
}),
TypeOrmModule.forFeature([StudyRoomRepository]),
],
}).compile();
studyRoomRepository = module.get<StudyRoomRepository>(StudyRoomRepository);
});-
์ฅ์
- ์ค์ DB ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ์ํํ๋ค.
- ๊ฐ๋ฐ ํ๊ฒฝ์ด ๋ฌ๋ผ์ ธ๋, ๋์ผํ ํ๊ฒฝ์์ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
- ํ ์คํธ ํ ์ปจํ ์ด๋๊ฐ ์ข ๋ฃ๋๋ฏ๋ก ๊น๋ํ ์ ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
-
๋จ์
- ์ปจํ ์ด๋๋ฅผ ๋์ฐ๊ณ ๊ด๋ฆฌํ๋ ์๊ฐ์ด ์ถ๊ฐ๋ก ์์๋๋ค.
- ํ ์คํธ ํ๊ฒฝ ์ค์ ์ด ๋ณต์กํ๋ค.
- ๋ณด๋ค ์ ๊ตํ ํตํฉ ํ ์คํธ์ ์ ํฉํ๋ค.
https://docs.nestjs.com/techniques/database
https://docs.nestjs.com/fundamentals/custom-providers
-
Repository
- DB์์ ์ํธ์์ฉ์ด ์์๋๋ก ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํด
- In-memory DB๋ ๋ค๋ฅธ DB ์์กด์ฑ์ ์ฃผ์ ํ์ฌ ํ ์คํธ
โ ๋ฐฐํฌ ํ๊ฒฝ์ ๋ชจ๋ฐฉํ์ฌ ์์ ์ฑ๊ณผ ํธํ์ฑ์ ๊ฒ์ฆํ๊ธฐ ์ํด ๋ค๋ฅธ DB๋ฅผ ์ฌ์ฉ
-
Service
- ๋น์ฆ๋์ค ๋ก์ง์ด ์๋ํ ๋๋ก ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํด
- Custom Repository๋ฅผ ์์กด์ฑ ์ฃผ์ ํ์ฌ ํ ์คํธ
โ Repository layer๋ฅผ Mocking
-
Controller
- API ์์ฒญ ๋ฐ ์๋ต์ด ์์๋๋ก ์ฒ๋ฆฌ๋๋์ง ํ์ธํ๊ธฐ ์ํด
- Custom Service๋ฅผ ์์กด์ฑ ์ฃผ์ ํ์ฌ ํ ์คํธ
โ Service layer๋ฅผ Mocking
- ๐ง ํ ๋น๋ฉ ๋ฐ ๋ธ๋์น ์ ๋ต
- ๐ง ์ฃผ์ ์ ์ ๋ฐ ํ ํ๋ฆฟ ์์ฑ
- ๐ป ํ๋ก์ ํธ ์ด๋ฆ ๋ฐ ๋ก๊ณ ์ ํ๊ธฐ
- ๐ป ๊ธฐํ ์ธ๋ถ์ฌํญ ์ ํ๊ธฐ
- ๐ป ๊ธฐ์ ์คํ ์์
- ๐ป ๋ฐฑ๋ก๊ทธ ์ด์ ์์ฑ
- ๐ป ๊นํ๋ธ ์ํค
- ๐ ๊ธฐํ ๋ฐ MVP ์ ์ง ํ์
- 2๏ธโฃ ์คํ๋ฆฐํธ 2 ์ฃผ๊ฐ ๊ณํ
- ๐งญ ๊ณตํต ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ
- ๐ฆบ ์ด๋ฒคํธ ํ๋ฆ๋
- ๐ฅ ๋ฐฑ์๋ ์๊ทธ๋๋ง ์๋ฒ ๋ฌธ์
- ๐ฝ ์คํ๋ฆฐํธ 5โ 6 ๋ฐฉํฅ์ฑ
- ๐ ์ต์ข ๋ฐํ ์ค๋น
- ๐ฟ Github Actions CD(์๋๋ฐฐํฌ)
- ๐ API
- โซ forEach๋ async๋ฅผ ๊ธฐ๋ค๋ ค์ฃผ์ง ์๋๋ค
- ๐งช WebRTC SFU ๋ฐ๋ชจ
- ๐ TURN ์๋ฒ ์ธ์ฆ ๋ฐฉ์
- ๐ ๏ธ [ํธ๋ฌ๋ธ์ํ ] /socket.io ๊ฒฝ๋ก๋ก ์ง์์ ์ผ๋ก HTTP ์์ฒญ์ ๋ณด๋ด๋ ๋ฌธ์
- ๐น WebRTC ํ์ํ์์์ ์นด๋ฉ๋ผ ์ํ์ ๋ฐ๋ฅธ ์์คํ ์์ ์ฌ์ฉ๋ ๋ถ์
- ๐ฅ ๋์นญ NAT์์์ WebRTC ์ฐ๊ฒฐ ์๋ฆฝ ๊ณผ์
- ๐ ํ ์คํธ ์ฝ๋์ ๋ชจํน