"볡μ‘ν μ²μ, 3μ€λ‘ λλ΄λ€." λνλ―Όκ΅ κ΅ν κ΅λ―Όλμμ²μκ³Ό μ²μ24μ λ°μ΄ν°λ₯Ό νκ³³μ λͺ¨μ, AIλ‘ μκΈ° μ½κ² λΆμνκ³ μ²λ¦¬ κ²°κ³Όλ₯Ό μ€μκ°μΌλ‘ μλ €μ£Όλ νλ«νΌμ λλ€.
Moraλ ν©μ΄μ Έ μλ μ²μ μ¬μ΄νΈ(κ΅ν, νμ λΆ)μ λ°μ΄ν°λ₯Ό ν΅ν© μμ§νμ¬ μ 곡ν©λλ€. λ¨μν λͺ©λ‘ μ 곡μ λμ΄, **OpenAI(GPT-4o-mini)**λ₯Ό νμ©ν΄ 2030 μΈλκ° μ½κΈ° μ¬μ΄ **'μΉ΄λλ΄μ€ νν'**λ‘ λ΄μ©μ μμ½/λΆμνλ©°, μ¬μ©μκ° κ΄μ¬ μλ μ²μμ μ²λ¦¬ κ²°κ³Ό(μ¬μ¬, λ΅λ³ λ±)κ° μ λ°μ΄νΈλλ©΄ μ΄λ©μΌλ‘ μλ¦Όμ μ 곡νμ¬ μ§μμ μΈ μ μΉ μ°Έμ¬λ₯Ό μ λν©λλ€.
- κ΅ν μ²μ (Type 1): Open APIμ μμ νμ±(Apache POI)μ κ²°ν©νμ¬ κ³Όκ±° λ°μ΄ν°λΆν° μ€μκ° νν©κΉμ§ λΉνμμ΄ μμ§ν©λλ€.
- μ²μ 24 (Type 0): APIκ° μλ μ λΆ μ²μ μ¬μ΄νΈμ νΉμ±μ κ³ λ €νμ¬, Selenium κΈ°λ°μ λμ ν¬λ‘€λ§μΌλ‘ λ°μ΄ν°λ₯Ό ν보ν©λλ€. (νμ¬ ν¬λ‘€λ§ μ¬μ© κ°λ₯ μ¬λΆλ₯Ό μ νν νμ νμ§ λͺ»νμ¬ κΈ°λ₯λ§ μκ³ μμ λ§λ€ μ€νμ λμ§ μκ³ μμ΅λλ€.)
- 볡μ‘νκ³ κΈ΄ μ²μ μλ¬Έμ GPT-4o-miniμκ² μ λ¬νμ¬ λ€μκ³Ό κ°μ ꡬ쑰νλ λ°μ΄ν°λ‘ λ³νν©λλ€.
- SubTitle: 20λ νκ²μ ν νΉ(Hooking) μμ λͺ©
- Summary: ν΅μ¬ λ΄μ© 3μ€ μμ½
- Tags: κΈμ μ ν¨κ³Ό / λΆμ μ μ°λ €μ¬ν λΆμ
- Needs: μ²μ κ°μ
- μμ λ°°μΉ(Midnight Batch): λ§€μΌ λ°€ μ€νλμ΄ λͺ¨λ μ²μμ μν(νμ¬ κ΅λ―Όλμμ²μ νμ )λ₯Ό μλμΌλ‘ μ΅μ νν©λλ€.
- λ¬μ± κ°μ§: κΈ°κ°μ΄ λλλ©΄
λ§κ°μνλ‘ λ³κ²½ν©λλ€. - κ²°κ³Ό μΆμ : κ΅ν μμ μμ€ν
μ
μμν νλΆ,λ³Ένμ μ²λ¦¬κ²°κ³Όλ₯Ό κ°μ§ν©λλ€. - μ²λ¦¬κ²°κ³Ό κ°μ§: κ΅λ―Όλμμ²μ OpenApi λ΄
<μ²μ μ²λ¦¬κ²°κ³Ό>ν μ€νΈλ₯Ό μ λ°μ΄νΈν©λλ€.
- λ¬μ± κ°μ§: κΈ°κ°μ΄ λλλ©΄
- μ΄λ©μΌ μλ¦Ό: μ²μ μ²λ¦¬κ²°κ³Ό μν λ³νκ° κ°μ§λλ©΄ ν΄λΉ μ²μμ μ€ν¬λ©ν μ μ λ€μκ² λΉλκΈ° μ΄λ©μΌμ λ°μ‘ν©λλ€.
- (νμ¬ κ΅λ―Όλμμ²μ νμ μΌλ‘ λͺ¨λ κΈ°λ₯μ΄ μμ λ§λ€ λμκ°λ©° κ²°κ³ΌμΆμ , μ²λ¦¬κ²°κ³Ό κ°μ§, μ΄λ©μΌ μλ¦Όμ μ²μ24λ μλμΌλ‘ κΈ°λ₯ ꡬνμ λμ΄μμΌλ μμ λ§λ€ λμκ°κ³ μμ§λ μμ)
| Category | Technology | Description |
|---|---|---|
| Framework | Spring Boot 3.4.1 | ν΅μ¬ μλ² νλ μμν¬ |
| Language | Java 17 | κΈ°λ³Έ κ°λ° μΈμ΄ |
| Database | MySQL 8.0 | λ©μΈ λ°μ΄ν°λ² μ΄μ€ (RDBMS) |
| ORM | Spring Data JPA | κ°μ²΄-κ΄κ³ λ§€ν λ° μΏΌλ¦¬ μ²λ¦¬ (Hibernate 6.6.4) |
| Security | Spring Security & OAuth2 | μΈμ¦/μΈκ° λ° κ΅¬κΈ μμ λ‘κ·ΈμΈ κ΅¬ν |
| API Docs | Swagger (SpringDoc) | REST API λͺ μΈμ μλν |
| Category | Technology | Description |
|---|---|---|
| Crawling | Selenium | λμ μΉ νμ΄μ§(μ²μ24, κ΅ν μμΈ) λ°μ΄ν° μμ§ λ° λ΄ νμ§ μ°ν |
| Data Parsing | Apache POI | λμ©λ μμ λ°μ΄ν°λ₯Ό νμ±νμ¬ μ΄κΈ° λ°μ΄ν° μ μ¬ ν¨μ¨ν |
| Batch | Spring @Async | ν¬λ‘€λ§, μν μ λ°μ΄νΈ, λ©μΌ λ°μ‘ λ± μ₯κΈ° μ€ν μμ λΉλκΈ° μ²λ¦¬ |
| AI | OpenAI API (GPT-4o-mini) | μ²μ μλ¬Έ λΆμ, ν νΉ(Hooking) 문ꡬ μμ±, κΈμ /λΆμ μμΈ μΆμΆ |
| Category | Technology | Description |
|---|---|---|
| JavaMailSender | μ²μ μν/κ²°κ³Ό λ³κ²½ μ λΉλκΈ° μλ¦Ό λ©μΌ λ°μ‘ | |
| Server | AWS EC2 (Ubuntu) | ν΄λΌμ°λ λ°°ν¬ νκ²½ |
| Web Server | Nginx | 리λ²μ€ νλ‘μ λ° νμμμ μ€μ |
-
Petition
-
id: Long (not null, unique)
-
title: String (not null, unique)
-
type: Integer (not null) β μ²μ24, κ΅λ―Όλμμ²μ / 0,1
-
status: Integer (not null) β 0,1 (μ§ν, μ’ λ£)
-
subTitle: String (not null)
-
petitionNeeds: String (not null)
-
petitionSummary: String (not null)
-
result: String (not null)
-
category: String (not null) β λ¬΄μ¨ λΆμΌμΈμ§
-
finalDate: LocalDateTime
-
voteStartDate: LocalDateTime (not null)
-
voteEndDate: LocalDateTime (not null)
-
positiveEx: String (not null)
-
negativeEx: String (not null)
-
good: Integer (not null) β μ’μμ
-
bad: Integer (not null) β μ«μ΄μ
-
allows: Integer (not null) β λμμ μ(λ§€μΌ μ λ°μ΄νΈ)
-
url: String (not null)
-
department: String (not null) β μκ΄μ
-
Laws
-
id: Long (not null, unique)
-
title: String (not null, unique)
-
summary: String(not null)
-
**LawsLink
-
id: Long (pk)
-
petId: UUID (not null)
-
lawId: Long (not null)
-
User
-
id: UUID (unique, not null)
-
name: String (unique, not null)
-
email: String (not null, unique)
-
age: Integer (not null)
-
status: Integer (not null) β 4κ°μ§ μν
-
Scrap
-
id: Long (pk)
-
petId: Long (not null)
-
userId: UUID (not null)
-
Comment
-
id: Long (not null,unique)
-
petId: Long (not null)
-
userId: UUID (not null)
-
body: String (not null)
-
Likes
-
id: Long (pk)
-
petId: Long (not null)
-
userId: UUID (not null, unique)
-
like: Integer (not null) β μ«μ΄μ, μ’μμ / -1,1
- λ¬Έμ : 'μ²μ24' μ¬μ΄νΈλ νμ΄μ§λ§λ€ μ λͺ©κ³Ό λ μ§ μμμ μμΉκ° λ―ΈμΈνκ² λ€λ₯΄κ±°λ, λΆνμν λ±μ§ ν μ€νΈκ° μμ¬ μμ΄ μ νν νμ±μ΄ μ΄λ €μ μ΅λλ€.
- ν΄κ²°: νΉμ ν΄λμ€λͺ
μ μμ‘΄νλ λμ , "μ²λ¦¬κΈ°κ΄"μ΄λΌλ κ³ μ ν
μ€νΈλ₯Ό κΈ°μ€μΌλ‘ XPathμ
preceding(μλ°©ν₯ νμ) λ¬Έλ²μ μ¬μ©νμ¬ μ λͺ© μμΉλ₯Ό λμ μΌλ‘ μΆμ νλ λ‘μ§μ ꡬννμ΅λλ€. μ΅νμ μλ¨μΌλ‘ μ 체 ν μ€νΈ νμ± λ‘μ§μ μΆκ°νμ¬ μμ μ±μ ν보νμ΅λλ€.
- λ¬Έμ : λ‘컬 κ°λ° νκ²½(KST)κ³Ό λ¬λ¦¬ AWS EC2(UTC) λ°°ν¬ μ μ²μ λ μ§κ° 9μκ° μ μΌλ‘ κΈ°λ‘λλ λ°μ΄ν° μ ν©μ± λ¬Έμ κ° λ°μνμ΅λλ€.
- ν΄κ²°:
- Ubuntu μλ² νμμ‘΄ λ³κ²½ (
timedatectl set-timezone Asia/Seoul) - JVM μ€ν μ΅μ
μΆκ° (
-Duser.timezone=Asia/Seoul) - μ ν리μΌμ΄μ
μμ μμ (
@PostConstruct)μTimeZone.setDefault()μ€μ -> 3μ€ μμ μ₯μΉλ₯Ό ν΅ν΄ λ°μ΄ν°μ μκ° μ νμ±μ ν보νμ΅λλ€.
- Ubuntu μλ² νμμ‘΄ λ³κ²½ (
- λ¬Έμ : μμ² κ±΄μ μ²μ μνλ₯Ό μ λ°μ΄νΈνκ³ μ΄λ©μΌμ λ°μ‘νλ κ³Όμ μμ λ¨μΌ μ€λ λ μ²λ¦¬ μ νμμμμ΄ λ°μνμ΅λλ€.
- ν΄κ²°:
@Asyncμ΄λ Έν μ΄μ μ νμ©νμ¬ ν¬λ‘€λ§, μν 체ν¬, μ΄λ©μΌ λ°μ‘μ λ³λμ μ€λ λ νμμ λ³λ ¬λ‘ μ²λ¦¬νλλ‘ μν€ν μ²λ₯Ό κ°μ νμ΅λλ€. Selenium μΈμ€ν΄μ€ λν μ¬μ¬μ© λ‘μ§μ μ μ©νμ¬ λ¦¬μμ€ μ μ λ₯Ό μ€μμ΅λλ€.
- Java 17
- Gradle 8.x μ΄μ
- MySQL 8.0
git clone [https://github.com/Club-PARD/00_BE.git](https://github.com/Club-PARD/00_BE.git)
cd morasrc/main/resources/ κ²½λ‘μ application.yml νμΌμ μμ±νκ³ μλμ κ°μ΄ λ°μ΄ν°λ² μ΄μ€, JWT, OAuth2 ν΄λΌμ΄μΈνΈ μ 보 λ±μ μ€μ ν©λλ€.
spring:
application:
name: youngyoung.server.mora
logging:
level:
root: info
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://{DB}:3306/YY_mora?serverTimezone=UTC&characterEncoding=UTF-8&serverTimezone=Asia/Seoul
username: {name}
password: {PW}
mail:
host: smtp.gmail.com
port: 587
username: "{EMAIL}"
password: "{PW}"
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
jpa:
show-sql: true
database-platform: org.hibernate.dialect.MySQLDialect
database: mysql
hibernate:
ddl-auto: update
generate-ddl: false
properties:
hibernate:
format_sql: true
enable_lazy_load_no_trans: true
security:
oauth2:
client:
registration:
google:
client-id: {ID}
client-secret: {SECRET}
redirect-uri: {BASE}/login/oauth2/code/google
scope:
- profile
- email
openai:
api:
key: {KEY}
model: gpt-4o-mini
open-api:
assembly:
key: {KEY}
logging:
level:
org.springframework.security: DEBUG
org.springframework.web.client.RestTemplate: DEBUG
org.springframework.security.oauth2.client: DEBUGμ ν리μΌμ΄μ
μ΄ μ€νλλ©΄ https://00-fe.vercel.appμμ νμΈν μ μμ΅λλ€.
Swagger UIλ₯Ό ν΅ν΄ λͺ¨λ API μλν¬μΈνΈμ λͺ μΈλ₯Ό νμΈν μ μμ΅λλ€.
- Swagger UI:
https://moragora.site/swagger-ui/index.html#/
- Petition (
/petition):GET /{id}: μ²μ μμΈ μ 보 μ‘°νGET /cardNews: μ²μ λͺ©λ‘(μΉ΄λ λ΄μ€) μ‘°νPOST /likes: μ²μ 곡κ°/λΉκ³΅κ° μ²λ¦¬POST /comment: λκΈ μμ±GET /comment/{id}: λκΈ μ‘°νPOST /scrap/{id}: μ²μ μ€ν¬λ©
- User (
/user):POST /signUp: νμκ°μGET /me: λ΄ μ 보 μ‘°νGET /scrap: μ€ν¬λ©ν μ²μ μ‘°νDELETE /delete: νμ νν΄