백엔드 연속 채굴 시스템, 왜 이렇게 복잡할까?
백엔드 연속 채굴 시스템, 왜 이렇게 복잡할까?
채굴 시스템에선 무엇이 먼저 움직일까?
자산 거래나 전자지갑 기능만 생각했던 분들에게 '채굴 기능'은 다소 생소할 수 있습니다. 그런데 실제로 블록체인 기반 시스템에선 채굴(Mining) 기능이 핵심 축 중 하나예요. 특히 비트코인이나 이더리움과 같은 Layer 1 계열이 아니더라도, 자체적인 보상 시스템을 갖춘 프로젝트에선 연속 채굴 기능이 뒷단에서 잘 짜여 있어야 전체 구조가 안정적으로 작동합니다.
프론트엔드에서 버튼을 누르면 백엔드가 돌아가고, 그 사이에 실시간으로 채굴 상태를 반영하며 보상 정보를 갱신해 줘야 하죠. 이런 작업은 단발성 요청이 아닌 **지속적인 타이머 기반 요청(interval)**으로 이뤄지기 때문에, 단순한 API 호출과는 전혀 다른 차원의 논리 구조가 필요합니다.
왜 리로드 함수가 중요한가?
제가 금융권에서 개발자들과 실시간 자동 매매 시스템을 다루던 때가 있었어요. 비슷한 개념입니다. 핵심은 '값이 바뀌는 상황'을 실시간으로 감지하고 사용자에게 반영해주는 거예요. 이때 사용되는 게 바로 리로드 함수(reloadAmount) 같은 반복 호출 구조입니다.
채굴이라는 행위는 시간의 흐름과 함께 '무언가 결과물'이 쌓이는 행위잖아요? 이걸 보여주기 위해선 일정 주기로 화면을 갱신해줘야 하고, 여기서 사용하는 게 setInterval()
같은 비동기 주기 호출입니다. 사용자가 ‘스타트’를 누른 순간부터 이 함수가 계속 돌아가면서 현재 상태를 서버에 물어보고 답을 받아오게 되는 거죠.
사용자의 행동을 어떻게 감지하는가?
중요한 건 사용자가 조회 버튼을 누르지 않아도 채굴이 시작되도록 만들어야 한다는 점이에요. 즉, '예외 상황'에 대비한 조건문이 필요하죠. 전역 변수로 등록된 startCoinAmount
의 초기값이 0일 경우를 가정해서, 최초 리스폰스 값을 그 값으로 설정하는 로직이 들어갑니다.
이게 없으면 시스템은 항상 ‘0에서 시작’한다고 착각하게 돼요. 마치 지갑에 원래 100만 원 있었는데, 그걸 무시하고 "지금부터 시작하겠습니다" 하고 넘어가는 셈이죠. 실질적인 보상액을 계산하는 데 있어 오류가 생길 수밖에 없습니다.
보상금 계산의 본질, 단순한 산수 같지만?
사용자 입장에서 채굴 보상금이 얼마인지 보여주는 건 굉장히 중요한 요소입니다. 시작할 때 코인이 얼마였고, 지금은 얼마인지. 이 둘의 차이를 보여주면 사용자는 “내가 뭔가 얻었구나”라고 인식하게 되죠.
이때 등장하는 것이 parseFloat()
같은 형변환 메소드입니다. JSON 형식으로 받은 값은 대부분 문자열이기 때문에, 실수 또는 정수형으로 바꿔줘야 정상적인 계산이 가능해요. 실제로 채굴량이 소수점 아홉째 자리까지 나오기 때문에 정확한 수치 계산이 필수죠.
전자지갑 주소는 왜 꼭 필요할까?
당연한 얘기지만, 채굴 보상은 ‘어디로 보낼지’ 알아야 처리가 가능합니다. 프론트엔드에선 지갑 주소가 없을 경우 채굴을 시작하지 않도록 하고, 백엔드에선 이 지갑 주소가 반드시 DB에 등록된 사용자 소유임을 확인하는 절차가 있어야 해요.
제가 예전에 비트코인 자동 트레이딩 봇을 만들던 시절, 지갑 주소 없이 수익만 계산해서 출력해주는 모듈을 돌렸다가 수익 분배가 안 돼서 다시 전체 구조를 손봤던 기억이 납니다. 이 구조는 단순해 보여도 실전에서 굉장히 민감하게 작동합니다.
스레드(Thread)는 왜 필요할까?
백엔드에선 threading
을 이용해서 채굴 기능을 메인 프로세스와 분리된 상태로 작동시켜야 해요. 그렇지 않으면 사용자가 채굴 버튼을 누르는 순간, 서버 전체가 멈춰버릴 수 있습니다.
정확히 말하면, Flask 같은 웹 프레임워크는 요청을 받아 응답하는 동안만 일하는 구조인데, 채굴은 몇 분이든 몇 시간이든 계속 작업을 해야 하죠. 이걸 해결하려면 메인 응답은 빠르게 처리하고, 채굴은 ‘다른 공간’에서 돌아가야 합니다. 그걸 위한 게 바로 스레드입니다.
Flask의 컨텍스트 오류, 왜 자주 발생할까?
여기서 가장 많은 분들이 실수하는 부분이 나옵니다. 스레드 안에서 DB 작업이나 Flask 애플리케이션 객체를 사용하는 경우, RuntimeError: Working outside of application context
라는 에러가 뜹니다.
이건 왜 생기냐면, Flask는 요청(Request) 기반의 컨텍스트(Context) 위에서만 동작하도록 설계되어 있어요. 스레드처럼 ‘외부’에서 무언가 작업을 하려고 하면 “너 지금 내 컨트롤 범위 밖이야”라고 에러를 내뿜는 거죠.
해결법은 간단합니다. with app.app_context():
라는 구문을 써서 해당 작업 블록을 Flask의 관리 하에 놓는 거죠. 그 안에서 DB 작업을 안전하게 진행할 수 있습니다.
제대로 작동하게 하려면 결국 ‘앱 객체’가 필요하다
이쯤 되면 앱(App) 객체를 생성하는 구조를 어떻게 만들었느냐가 핵심이에요. 보통 create_app()
함수 안에서 Flask 객체를 만들어 반환하도록 구성합니다. 이걸 통해 앱을 모듈화하고, 블루프린트 별로 나눌 수 있죠.
스레드 안에서 이 앱 객체를 호출해서 컨텍스트를 열어줘야 하니까, 반드시 create_app()
을 호출해서 앱을 가져와야 합니다. 저 같은 경우, 프로젝트 구조를 처음 짤 때부터 이 부분을 가장 먼저 설계합니다. 그래야 스레드든 테스트든 자유롭게 작동하니까요.
채굴이 끝나지 않는 구조의 위험성
이 시스템은 자동 반복이 핵심입니다. setInterval
로 계속 서버에 요청을 보내서 현재 채굴 상태를 확인하고, 그 값을 업데이트하죠. 그런데 정작 중지 기능이 없으면? 계속해서 채굴이 돌아가면서 서버 리소스를 다 빨아먹습니다.
제가 테스트 중에 CPU가 과열돼서 본체에서 팬 소리가 미친 듯이 나는 걸 직접 겪었습니다. 개발 환경이 데스크탑이면 그래도 괜찮은데, 랩탑에선 정말 위험해요. 이때 필요한 게 바로 **채굴 중지 기능(Stop Mining)**입니다.
추후에는 setInterval
을 clear하고, 서버 측에서도 스레드를 강제로 종료하는 구조를 구현해야 합니다. 지금은 그냥 CTRL + C
로 서버 자체를 종료했지만, 이건 임시 방편일 뿐이에요.
요약해보자면
-
채굴 기능은 단발성이 아닌 지속성 로직이 필요하다.
-
사용자 인터페이스는 조건문으로 ‘조회하지 않은 상태’를 커버해야 한다.
-
서버는 스레드를 활용해 메인 로직과 분리돼야 한다.
-
Flask는 외부 컨텍스트 접근 시 반드시
app_context
로 감싸야 한다. -
리로드 및 보상 계산은 문자열을 실수로 형변환하는 게 핵심이다.
-
지갑 주소는 필수이며, DB 연동 전제하에 채굴이 시작돼야 한다.
-
중지 기능이 없으면 서버가 무한히 돌아가며 리스크가 커진다.
이 정도만 알고 시작해도, 여러분이 만드는 채굴 시스템은 단순한 코드 짜깁기가 아닌, 하나의 ‘작동 가능한 경제 시스템’으로 발전할 수 있습니다. 단순한 기능 하나가 아니라, 그 안에 데이터 흐름, 사용자 경험, 자원 최적화 등 다양한 요소가 녹아들어 있다는 걸 꼭 인식하셔야 합니다.
포스팅 마무리하며, 전 여의도 증권사 출신으로 지금은 실전 블록체인 기반 투자와 개발 모두를 병행 중인데요. 돈 되는 시스템은 결국 ‘제대로 설계된 시스템’에서 나옵니다. 채굴도 예외는 아니에요. 투자도 코딩도, 결국 디테일에서 차이가 나는 거니까요.
댓글
댓글 쓰기