What's the correct way to make it so that node_load both loads and caches additional, custom data?
The hook you're after is called hook_node_load(). I think the example from that page is probably the easiest to understand:

function hook_node_load($nodes, $types) {
  $result = db_query('SELECT nid, foo FROM {mytable} WHERE nid IN(:nids)', array(':nids' =&gt; array_keys($nodes)));
  foreach ($result as $record) {
    $nodes[$record-&gt;nid]-&gt;foo = $record-&gt;foo;
  }
}


The example loads some custom data from a table, based on the ids of the nodes passed into the function. It then loops through the query results, and adds the custom data to the relevant node objects.

When you call node_load() or node_load_multiple() for nodes that haven't been cached yet, your hook_node_load() function will be called and the custom data added. 

Next time you call those functions for the same nodes your hook won't be called, but the node objects returned from the static cache will already have your custom data attached to them.