MovieThree
moviethree.synology.me



#주의 절대로 최신 버전으로 코딩을 하지 맙시다. 레포가 굉장히 적으며 수 많은 버그에 힘듭니다...
목차
사용된 스택
Nas : DS220+
OS: DSM 7.1.1
Docker
nginx:1.9.15 - alpine
node: 16.20.0
jenkins:jdk17
redis
mariaDB 10
Spring-Boot: 3.0.1
jdk: 17
Kotlin: 1.7.22
Jsoup:1.15.4
querydsl:5.0.0:jakerta
coroutine:1.7.1
Npm: 16.20.0
React
Typescript
대략적인 구조와 설명

moviethree.synology.me로 접속
- http로 접속시 nginx가 https로 리다이렉트 시킵니다.
- https로 접속 했다면 nginx가 location을 통해 /api 요청인지 프론트 요청인지를 판별합니다.
- location /api 라면 localhost:8080으로 proxy pass를 해줍니다.
- location /라면 localhost:3000으로 proxyt pass를 해줍니다.

Git 업데이트
- GitHub main push 시 Git Action으로 GitLab에 push를 동일하게 날립니다.
- GitLab main에 push 가 들어올 경우 Jenkins에 push가 왔다는 알림을 보냅니다.
- Jenkins 에서 spring build와 node build를 실행 합니다.
- build된 백 엔드 서버는 docker file을 읽어 docker로 실행합니다.
- 프론트 서버의 경우 build가 되면 nodemon이 걸려 있는 폴더로 이동합니다.
- nodemon이 프론트의 변경점을 확인하고 반영 해줍니다.

DB
- Jwt관리 용 Redis
- 일반적인 정보 저장 용 MariaDB
Frond End 설정
package.json
{
"name": "threemovieweb",
"version": "0.1.0",
"private": true,
"dependencies": {
"@apollo/react-hooks": "^4.0.0",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mui/icons-material": "^5.11.11",
"@mui/lab": "^5.0.0-alpha.124",
"@mui/material": "^5.11.14",
"@mui/x-date-pickers": "^6.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.11",
"@types/qs": "^6.9.7",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3",
"apollo-boost": "^0.4.9",
"axios": "^1.3.4",
"dayjs": "^1.11.7",
"eslint-plugin-unused-imports": "^2.0.0",
"graphql": "^16.6.0",
"prettier": "^2.8.2",
"qs": "^6.11.1",
"react": "^18.2.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.6.2",
"react-scripts": "5.0.1",
"recoil": "^0.7.7",
"sass": "^1.62.0",
"styled-components": "^5.3.9",
"styled-reset": "^4.4.5",
"swiper": "^9.1.1",
"typescript": "^4.9.4",
"v6": "^0.0.0",
"web-vitals": "^2.1.4",
"yarn": "^1.22.19"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint": "8.22.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0"
},
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"src/types"
]
}
}
eslintrc
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'prettier', 'import', 'unused-imports'],
extends: [
'airbnb',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
camelcase: 'off',
'linebreak-style': 0,
'import/prefer-default-export': 0,
'prettier/prettier': 0,
'import/extensions': 0,
'no-use-before-define': 0,
'import/no-unresolved': 0,
'import/no-extraneous-dependencies': 0,
'no-shadow': 0,
'react/prop-types': 0,
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'jsx-a11y/no-noninteractive-element-interactions': 0,
'react/function-component-definition': [2, { namedComponents: 'arrow-function' }],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
pathGroups: [
{
pattern: 'react',
group: 'builtin',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{ vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
],
},
};
prettierrc
{
"singleQuote": true,
"semi": true,
"useTabs": false,
"tabWidth": 4,
"trailingComma": "all",
"printWidth": 120,
"arrowParens": "always"
}

Back End 설정
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.0.1"
id("io.spring.dependency-management") version "1.1.0"
id("org.jetbrains.kotlin.plugin.noarg") version "1.7.22"
kotlin("jvm") version "1.7.22"
kotlin("plugin.spring") version "1.7.22"
kotlin("plugin.jpa") version "1.7.22"
kotlin("kapt") version "1.7.22"
}
noArg {
annotation("jakarta.persistence.Entity")
}
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}
group = "com.threemovie"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("org.springframework.boot:spring-boot-starter-graphql")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.jsoup:jsoup:1.15.4")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
implementation("org.json:json:20230227")
implementation("org.ehcache:ehcache:3.10.8")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4")
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
implementation("com.github.f4b6a3:ulid-creator:5.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
}
tasks.withType<Jar> {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
application.properties (메인 properties와 graphql 설정)
spring.profiles.include=smtp-config, db-config, jwt-config, security-config
spring.graphql.path=/api/graphql
spring.graphql.graphiql.path=/api/graphiql
spring.graphql.graphiql.enabled=true
spring.graphql.schema.locations=classpath:graphql/**/
spring.graphql.schema.file-extensions=.graphqls,.gqls
spring.graphql.schema.introspection.enabled=true
spring.graphql.schema.printer.enabled=true
spring.graphql.websocket.connection-init-timeout=60s
application-db-config.properties (db 아이디와 접속 설정)
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.datasource.hikari.data-source-properties.rewriteBatchedStatements=true
spring.datasource.hikari.data-source-properties.useConfigs=maxPerformance
spring.jpa.properties.hibernate.jdbc.batch_size=300
spring.datasource.hikari.validationTimeout=300000
spring.datasource.hikari.connection-timeout=58000
spring.datasource.hikari.max-lifetime=580000
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.order_inserts=true
spring.datasource.url=jdbc:mariadb://호스트:포트/DB명?serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=utf8mb4
spring.datasource.username=아이디
spring.datasource.password=비밀번호
spring.data.redis.host=호스트
spring.data.redis.port=포트
spring.data.redis.password=비밀번호
application-jwt-config.properties (jwt token 발급 관련 설정)
jwt.secret.key=비밀키
jwt.secret.refreshtoken-validity-in-seconds=50400
jwt.secret.access-token-validity-in-seconds=7200
application-security-config.properties (spring-security 설정)
spring.security.user.name=이름
spring.security.user.password=비밀번호
application-smtp-config.properties (메일 보내기 관련 설정 - 네이버)
spring.mail.host=smtp.naver.com
spring.mail.default-encoding=UTF-8
spring.mail.port=465
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.ssl.trust=smtp.naver.com
spring.mail.username=아이디
spring.mail.password=비밀번호

정리
처음으로 만들어 본 제대로 된 웹 서버 겸 프로젝트 이며 부족함이 많습니다.
코드도 지저분 할 수 있으며 절대로 최신 버전의 스프링 부트와 코틀린으로 코딩을 추천드리지 않습니다..
일주일에 시간이 날 때마다 작성할 예정입니다.
피드백을 말씀 해주시면 감사하겠습니다!
https://github.com/Hangeulkim/ThreeMovie
GitHub - Hangeulkim/ThreeMovie
Contribute to Hangeulkim/ThreeMovie development by creating an account on GitHub.
github.com
'Project > ThreeMovie(영화리뷰및예약도우미)' 카테고리의 다른 글
| 2. 비동기, 코루틴, 크론 탭 스케줄러 [kotlin / jsoup] (0) | 2023.06.23 |
|---|---|
| 크롤링에 batch update(bulk insert)와 coroutine으로 속도 올리기 [Kotlin + spring boot] (1) | 2023.05.25 |
| ShowTime 데이터 저장 시간 줄이기 (0) | 2023.03.14 |
| JpaRepository join using QueryProjection with DTO(kotlin) (0) | 2023.03.13 |
| JpaRepository saveAll(Insert) 사용하기 (0) | 2023.03.12 |
댓글