본문 바로가기

(개인Project)_개발/PLC-PC 연결

[Program][보족] 설정이 계속 날아가는 이유 (localStorage의 한계와 DB 전환 시점)

반응형

이 글이 나한테 해당되는지 먼저 확인해보세요

이런 상황이 하나라도 해당된다면, 이 글이 도움이 될 겁니다.

  • 브라우저에 저장한 데이터를 서버에서 읽으려고 했는데 안 됐다
  • 화면을 새로고침하거나 다른 브라우저로 접속하면 설정이 날아간다
  • 데이터를 날짜별로 조회하거나 정렬하고 싶은데 localStorage로는 한계가 있다
  • SQLite나 DB가 필요할 것 같은데 어떻게 시작해야 할지 모르겠다
  • "지금은 localStorage면 충분하지 않나?"라는 생각이 드는데 확신이 없다

PLCLink를 만들면서 localStorage로 시작해서 SQLite로 넘어간 과정을 정리했습니다.

어느 시점에 DB가 필요해지는지, 전환이 어렵지는 않은지 실제 경험 기준으로 이야기합니다.


용어 먼저 짚고 넘어갈게요

localStorage란

브라우저 안에 내장된 간단한 저장 공간입니다.

크롬, 엣지 같은 인터넷 창 안에서만 접근할 수 있어요.

메모장에 뭔가를 적어두는 것과 비슷한데, 브라우저를 닫아도 지워지지 않는 메모장이라고 생각하면 됩니다.

설정값이나 간단한 상태를 저장할 때 주로 씁니다.

 

SQLite란

파일 하나로 동작하는 가벼운 데이터베이스입니다.

MySQL 같은 DB는 별도 서버가 필요한데, SQLite는 .db 파일 하나만 있으면 됩니다.

설치 없이 쓸 수 있어서 소규모 툴이나 현장 도구에 잘 맞습니다.

Python에서는 별도 설치 없이 import sqlite3로 바로 사용할 수 있어요.

 

왜 둘을 비교하나

localStorage는 브라우저(프론트엔드)에서만 접근 가능합니다.

서버(백엔드)에서는 읽을 수 없어요. SQLite는 반대로 서버에서 읽고 쓰는 파일입니다.

브라우저에서는 직접 접근 안 되고, API를 통해서만 읽습니다.

누가 데이터를 사용해야 하느냐에 따라 선택이 달라집니다.


처음에는 localStorage로 충분했다

Phase 0에서 PLCLink를 처음 만들 때, 저장해야 할 게 별로 없었습니다.

어떤 IP의 PLC에 연결할 건지, 어떤 주소를 모니터링할 건지

이 정도가 전부였어요. 브라우저를 닫아도 설정이 유지되면 충분했고, localStorage는 그 역할을 잘 해줬습니다.

// localStorage 사용 예시
localStorage.setItem('plc_ip', '192.168.0.10');
localStorage.setItem('plc_port', '5000');

const ip = localStorage.getItem('plc_ip');

간단하고 빠릅니다. 서버 없이 브라우저 안에서 저장과 조회가 끝납니다.

Phase 1에서 IO 포인트 등록, 데이터 리스트 설정을 추가할 때도 localStorage를 그대로 썼습니다.

배열을 JSON으로 직렬화해서 저장하는 방식이었어요.

// 등록된 IO 포인트 저장
const points = [
  { address: 'Y3920', label: '인버터 RUN' },
  { address: 'Y3921', label: '인버터 STOP' },
];
localStorage.setItem('io_points', JSON.stringify(points));

이때까지는 문제가 없었습니다.

데이터가 많지 않았고, 서버가 이 데이터를 읽을 필요도 없었으니까요.


한계가 보이기 시작한 순간

Phase 1.5에서 알람 기능을 추가하면서 첫 번째 벽을 만났습니다.

알람 룰은 localStorage에 저장하면 됩니다.

사용자가 브라우저에서 등록하고, 브라우저에서 읽으면 되니까요.

 

그런데 알람 이력은 달랐습니다.

이전 글에서 설명한 것처럼, 알람 감시는 서버에서 합니다.

서버가 PLC를 1초마다 읽고, 조건이 맞으면 이력을 기록해야 합니다.

그런데 이력을 어디에 저장하냐는 질문이 남았습니다.

 

localStorage에 저장한다고 가정해봅시다.

서버가 "알람 발생했다"는 사실을 알았습니다.

이걸 localStorage에 저장하려면... 서버가 브라우저의 localStorage에 접근해야 합니다.

그런데 그게 안 됩니다.

localStorage는 브라우저 안에만 존재하는 공간이고, 서버는 거기에 손댈 수 없습니다.

냉장고 안에 있는 메모장에 뭔가 적어두고 싶은데, 집 밖에 있는 사람은 냉장고를 열 수 없는 것과 같습니다.

서버와 브라우저는 서로 다른 공간에 있습니다.

 

두 번째 한계도 생겼습니다.

트리거 이력, 알람 발생 횟수, 날짜별 조회

이런 기능을 만들려면 데이터를 쿼리해야 합니다.

"오늘 발생한 알람만 보여줘", "이번 주 가장 많이 발생한 알람 Top 5" 같은 것들이요.

localStorage는 그냥 문자열 저장소입니다.

조건 검색, 정렬, 집계가 안 됩니다. 전체를 꺼내서 JavaScript로 직접 필터링해야 하는데, 데이터가 수천 건이 되면 현실적으로 어렵습니다.


SQLite로 전환한 이유 : 딱 두 가지

복잡한 이유가 있었던 건 아닙니다.

 

첫 번째 : 서버도 읽어야 한다

알람 이력, 트리거 스냅샷을 서버가 직접 기록해야 했습니다.

서버가 접근할 수 있는 저장소가 필요했어요.

SQLite 파일은 서버가 직접 읽고 쓰는 파일입니다.

 

두 번째 : 쿼리가 필요하다

날짜 필터, 정렬, 집계

이걸 직접 구현하지 않고 SQL 한 줄로 해결하고 싶었습니다.

-- 오늘 발생한 알람, 최신순
SELECT * FROM alarm_history
WHERE DATE(created_at) = DATE('now')
ORDER BY created_at DESC;

JavaScript로 필터링하는 것보다 훨씬 간단합니다.

두 이유 외에 다른 건 없었어요.

성능이 부족해서도 아니고, 유행해서도 아닙니다.


전환이 어렵지 않았던 이유

SQLite를 처음 써봤는데, 생각보다 진입 장벽이 낮았습니다.

Python에는 sqlite3가 기본 내장돼 있습니다.

설치가 필요 없어요. 파일 하나만 만들면 됩니다.

import sqlite3

# DB 파일 연결 (없으면 자동 생성)
conn = sqlite3.connect('plclink.db')
cursor = conn.cursor()

# 테이블 생성
cursor.execute('''
    CREATE TABLE IF NOT EXISTS alarm_history (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        rule_id INTEGER,
        label TEXT,
        value TEXT,
        active INTEGER DEFAULT 1,
        created_at TEXT DEFAULT (datetime('now'))
    )
''')
conn.commit()

기존에 localStorage에 JSON으로 저장하던 데이터를 테이블 구조로 옮기는 작업이었어요.

컬럼을 정의하고, INSERT / SELECT로 읽고 쓰는 방식으로 바꿨습니다.

데이터 구조가 단순하면 하루 이틀이면 전환됩니다.

복잡한 서버 설정이나 네트워크 설정이 없고, 파일 하나로 끝납니다.

현장 PC에서 돌리는 소규모 툴에는 이게 오히려 장점입니다.


그렇다면 localStorage는 언제 쓰나

지금도 localStorage를 씁니다.

SQLite로 전부 옮긴 건 아닙니다.

 

localStorage에 남겨둔 것:

  • 폴링 ON/OFF 상태 (새로고침해도 유지되길 원하는 UI 상태)
  • 마지막으로 선택한 메이커 (Keyence / Mitsubishi)
  • 연결 설정 (IP, 포트)

이것들은 서버가 읽을 필요가 없습니다.

그리고 브라우저가 기억해두는 게 자연스러운 정보들이에요.

설치된 앱이 내 설정을 기억하는 것처럼.

 

SQLite로 옮긴 것:

  • 알람 이력 (서버가 기록하니까)
  • 트리거 스냅샷 (서버가 기록하니까)
  • 알람 룰 (서버가 읽어야 조건 판단 가능)
  • 데이터 리스트 설정 (서버가 읽어야 폴링 가능)

구분 기준은 단순합니다.

서버도 이 데이터를 읽어야 하는가? → SQLite

브라우저만 쓰는 UI 상태인가? → localStorage


한 가지 주의할 점

SQLite는 동시에 여러 프로세스가 쓰기를 시도하면 충돌이 생길 수 있습니다.

PLCLink처럼 서버 하나가 쓰고, 여러 브라우저가 API를 통해 읽는 구조라면 문제없습니다.

그런데 여러 프로세스가 동시에 SQLite 파일에 직접 쓰기를 한다면 database is locked 오류가 납니다.

 

현장 PC에서 혼자 돌리는 소규모 툴 기준으로는 이 한계가 거의 문제가 되지 않습니다.

규모가 커지거나 여러 서버가 같은 DB를 공유해야 한다면 그때 PostgreSQL 같은 걸 고민하면 됩니다.

지금 당장 필요하지 않은 것 때문에 미리 복잡하게 만들 이유는 없어요.


정리 : localStorage vs SQLite 선택 기준

항목 localStorage SQLite
누가 접근하나 브라우저만 서버 (브라우저는 API 통해 간접 접근)
쿼리 (검색, 정렬) 불가 (직접 구현 필요) SQL로 가능
데이터 유지 브라우저별로 따로 저장 서버 파일 하나에 통합
설치 필요 없음 필요 없음 (Python 기본 내장)
적합한 데이터 UI 상태, 사용자 설정 이력, 룰, 서버가 읽는 설정
규모 한계 5~10MB 수준 수십만 건 이상도 문제없음

마치며

"그냥 브라우저에 저장하면 안 되나요?"

처음엔 그렇게 생각했습니다.

localStorage가 간단하고, 별도 설정이 없고, 빠르게 쓸 수 있으니까요.

실제로 초반에는 그게 맞는 선택이었습니다.

전환이 필요한 신호는 명확했습니다.

서버가 그 데이터를 읽어야 하는 순간, 그때 DB가 필요해집니다.

그 전까지는 localStorage로 충분합니다.

지금 만들고 있는 툴에서 서버가 데이터를 읽어야 할 일이 있다면, 그게 SQLite를 검토할 시점입니다.

반응형